From b96b707efa42ebcbc78e50fedcc29ccaf8dd1467 Mon Sep 17 00:00:00 2001 From: Tim Starling Date: Sat, 10 Jul 2004 03:09:26 +0000 Subject: [PATCH] OOP calling convention for database functions. DBMS abstraction implemented by means of functions instead of global variables, PostgreSQL object converted to a subclass instead of a drop-in replacement. Also the beginnings of a flexible table name feature. --- includes/Article.php | 614 ++++++++++++++++-------------- includes/Block.php | 75 ++-- includes/Database.php | 372 ++++++++++++++---- includes/DatabaseFunctions.php | 50 ++- includes/DatabasePostgreSQL.php | 479 ++++++++--------------- includes/DefaultSettings.php | 12 +- includes/DifferenceEngine.php | 51 +-- includes/EditPage.php | 18 +- includes/GlobalFunctions.php | 85 ++++- includes/Image.php | 39 +- includes/ImagePage.php | 44 +-- includes/LinkCache.php | 95 ++--- includes/LinksUpdate.php | 167 ++++---- includes/LoadBalancer.php | 43 ++- includes/LogPage.php | 69 ++-- includes/Math.php | 60 +-- includes/MessageCache.php | 22 +- includes/ObjectCache.php | 15 +- includes/OutputPage.php | 2 +- includes/PageHistory.php | 17 +- includes/Parser.php | 47 ++- includes/Profiling.php | 25 +- includes/QueryPage.php | 71 +++- includes/RawPage.php | 15 +- includes/SearchEngine.php | 13 +- includes/SearchUpdate.php | 10 +- includes/Setup.php | 17 +- includes/SiteStatsUpdate.php | 7 +- includes/Skin.php | 17 +- includes/SkinPHPTal.php | 4 +- includes/SpecialAllpages.php | 6 +- includes/SpecialAncientpages.php | 4 +- includes/SpecialContributions.php | 4 +- includes/SpecialListadmins.php | 3 +- includes/SpecialListusers.php | 5 +- includes/SpecialMaintenance.php | 163 ++++---- includes/SpecialNewpages.php | 2 +- includes/SpecialRandompage.php | 9 +- includes/SpecialStatistics.php | 25 +- includes/SpecialWatchlist.php | 13 +- includes/Title.php | 241 ++++++------ includes/ViewCountUpdate.php | 7 +- includes/WatchedItem.php | 23 +- index.php | 5 +- install-utils.inc | 3 +- texvc.phtml | 254 ++++++------ 46 files changed, 1828 insertions(+), 1494 deletions(-) diff --git a/includes/Article.php b/includes/Article.php index 1af07e3872..4a905e6bcf 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -9,6 +9,9 @@ require_once( 'CacheManager.php' ); +$wgArticleCurContentFields = false; +$wgArticleOldContentFields = false; + class Article { /* private */ var $mContent, $mContentLoaded; /* private */ var $mUser, $mTimestamp, $mUserText; @@ -140,7 +143,7 @@ class Article { $index = $matches[4]; if ( $machineID == 0 ) { # Current connection - $db =& wfGetDB(); + $db =& wfGetDB( DB_READ ); } else { # Alternate connection $db =& $wgLoadBalancer->getConnection( $machineID ); @@ -153,7 +156,7 @@ class Article { } } if ( $db->isOpen() ) { - $index = wfStrencode( $index ); + $index = $db->strencode( $index ); $res = $db->query( "SELECT blob_data FROM $dbName.$tblName WHERE blob_index='$index'", $fname ); $row = $db->fetchObject( $res ); $text = $row->text_data; @@ -309,12 +312,30 @@ class Article { } + function &getCurContentFields() { + global $wgArticleCurContentFields; + if ( !$wgArticleCurContentFields ) { + $wgArticleCurContentFields = array( 'cur_text','cur_timestamp','cur_user', 'cur_user_text', + 'cur_comment','cur_counter','cur_restrictions','cur_touched' ); + } + return $wgArticleCurContentFields; + } + + function &getOldContentFields() { + global $wgArticleOldContentFields; + if ( !$wgArticleOldContentFields ) { + $wgArticleOldContentFields = array( 'old_namespace','old_title','old_text','old_timestamp', + 'old_user','old_user_text','old_comment','old_flags' ); + } + return $wgArticleOldContentFields; + } # Load the revision (including cur_text) into this object function loadContent( $noredir = false ) { - global $wgOut, $wgMwRedir, $wgRequest, $wgIsPg, $wgLoadBalancer; + global $wgOut, $wgMwRedir, $wgRequest; + $dbr =& wfGetDB( DB_READ ); # Query variables :P $oldid = $wgRequest->getVal( 'oldid' ); $redirect = $wgRequest->getVal( 'redirect' ); @@ -340,16 +361,11 @@ class Article { $id = $this->getID(); if ( 0 == $id ) return; - $sql = 'SELECT ' . - 'cur_text,cur_timestamp,cur_user,cur_user_text,cur_comment,cur_counter,cur_restrictions,cur_touched ' . - "FROM cur WHERE cur_id={$id}"; - wfDebug( "$sql\n" ); - $res = wfQuery( $sql, DB_READ, $fname ); - if ( 0 == wfNumRows( $res ) ) { + $s = $dbr->getArray( 'cur', $this->getCurContentFields(), array( 'cur_id' => $id ), $fname ); + if ( $s === false ) { return; } - $s = wfFetchObject( $res ); # If we got a redirect, follow it (unless we've been told # not to by either the function parameter or the query if ( ( 'no' != $redirect ) && ( false == $noredir ) && @@ -372,14 +388,12 @@ class Article { } $rid = $rt->getArticleID(); if ( 0 != $rid ) { - $sql = 'SELECT cur_text,cur_timestamp,cur_user,cur_user_text,cur_comment,' . - "cur_counter,cur_restrictions,cur_touched FROM cur WHERE cur_id={$rid}"; - $res = wfQuery( $sql, DB_READ, $fname ); + $redirRow = $dbr->getArray( 'cur', $this->getContentFields(), array( 'cur_id' => $rid ), $fname ); - if ( 0 != wfNumRows( $res ) ) { + if ( $redirRow !== false ) { $this->mRedirectedFrom = $this->mTitle->getPrefixedText(); $this->mTitle = $rt; - $s = wfFetchObject( $res ); + $s = $redirRow; } } } @@ -395,20 +409,12 @@ class Article { $this->mTouched = $s->cur_touched; $this->mTitle->mRestrictions = explode( ',', trim( $s->cur_restrictions ) ); $this->mTitle->mRestrictionsLoaded = true; - wfFreeResult( $res ); } else { # oldid set, retrieve historical version - $wgLoadBalancer->force(-1); - $oldtable=$wgIsPg?'"old"':'old'; - $sql = "SELECT old_namespace,old_title,old_text,old_timestamp,". - "old_user,old_user_text,old_comment,old_flags FROM old ". - "WHERE old_id={$oldid}"; - $res = wfQuery( $sql, DB_READ, $fname ); - $wgLoadBalancer->force(0); - if ( 0 == wfNumRows( $res ) ) { + $s = $dbr->getArray( 'old', $this->getOldContentFields(), array( 'old_id' => $oldid ), $fname ); + if ( $s === false ) { return; } - $s = wfFetchObject( $res ); if( $this->mTitle->getNamespace() != $s->old_namespace || $this->mTitle->getDBkey() != $s->old_title ) { $oldTitle = Title::makeTitle( $s->old_namesapce, $s->old_title ); @@ -421,7 +427,6 @@ class Article { $this->mComment = $s->old_comment; $this->mCounter = 0; $this->mTimestamp = $s->old_timestamp; - wfFreeResult( $res ); } $this->mContentLoaded = true; return $this->mContent; @@ -430,14 +435,15 @@ class Article { # Gets the article text without using so many damn globals # Returns false on error function getContentWithoutUsingSoManyDamnGlobals( $oldid = 0, $noredir = false ) { - global $wgMwRedir, $wgIsPg; + global $wgMwRedir; if ( $this->mContentLoaded ) { return $this->mContent; } $this->mContent = false; - $fname = 'Article::loadContent'; + $fname = 'Article::getContentWithout'; + $dbr =& wfGetDB( DB_READ ); if ( ! $oldid ) { # Retrieve current version $id = $this->getID(); @@ -445,15 +451,11 @@ class Article { return false; } - $sql = 'SELECT ' . - 'cur_text,cur_timestamp,cur_user,cur_counter,cur_restrictions,cur_touched ' . - "FROM cur WHERE cur_id={$id}"; - $res = wfQuery( $sql, DB_READ, $fname ); - if ( 0 == wfNumRows( $res ) ) { + $s = $dbr->getArray( 'cur', $this->getCurContentFields(), array( 'cur_id' => $id ), $fname ); + if ( $s === false ) { return false; } - $s = wfFetchObject( $res ); # If we got a redirect, follow it (unless we've been told # not to by either the function parameter or the query if ( !$noredir && $wgMwRedir->matchStart( $s->cur_text ) ) { @@ -463,14 +465,12 @@ class Article { if( $rt && $rt->getInterwiki() == '' && $rt->getNamespace() != Namespace::getSpecial() ) { $rid = $rt->getArticleID(); if ( 0 != $rid ) { - $sql = 'SELECT cur_text,cur_timestamp,cur_user,' . - "cur_counter,cur_restrictions,cur_touched FROM cur WHERE cur_id={$rid}"; - $res = wfQuery( $sql, DB_READ, $fname ); + $redirRow = $dbr->getArray( 'cur', $this->getCurContentFields(), array( 'cur_id' => $rid ), $fname ); - if ( 0 != wfNumRows( $res ) ) { + if ( $redirRow !== false ) { $this->mRedirectedFrom = $this->mTitle->getPrefixedText(); $this->mTitle = $rt; - $s = wfFetchObject( $res ); + $s = $redirRow; } } } @@ -479,27 +479,24 @@ class Article { $this->mContent = $s->cur_text; $this->mUser = $s->cur_user; + $this->mUserText = $s->cur_user_text; + $this->mComment = $s->cur_comment; $this->mCounter = $s->cur_counter; $this->mTimestamp = $s->cur_timestamp; $this->mTouched = $s->cur_touched; $this->mTitle->mRestrictions = explode( ",", trim( $s->cur_restrictions ) ); $this->mTitle->mRestrictionsLoaded = true; - wfFreeResult( $res ); } else { # oldid set, retrieve historical version - $oldtable=$wgIsPg?'"old"':'old'; - $sql = "SELECT old_text,old_timestamp,old_user,old_flags FROM $oldtable " . - "WHERE old_id={$oldid}"; - $res = wfQuery( $sql, DB_READ, $fname ); - if ( 0 == wfNumRows( $res ) ) { + $s = $dbr->getArray( 'old', $this->getOldContentFields(), array( 'old_id' => $oldid ) ); + if ( $s === false ) { return false; } - - $s = wfFetchObject( $res ); $this->mContent = Article::getRevisionText( $s ); $this->mUser = $s->old_user; + $this->mUserText = $s->old_user_text; + $this->mComment = $s->old_comment; $this->mCounter = 0; $this->mTimestamp = $s->old_timestamp; - wfFreeResult( $res ); } $this->mContentLoaded = true; return $this->mContent; @@ -517,7 +514,8 @@ class Article { { if ( -1 == $this->mCounter ) { $id = $this->getID(); - $this->mCounter = wfGetSQL( 'cur', 'cur_counter', "cur_id={$id}" ); + $dbr =& wfGetDB( DB_READ ); + $this->mCounter = $dbr->getField( 'cur', 'cur_counter', "cur_id={$id}" ); } return $this->mCounter; } @@ -543,14 +541,15 @@ class Article { { global $wgOut; if ( -1 != $this->mUser ) return; + + $fname = 'Article::loadLastEdit'; - $sql = 'SELECT cur_user,cur_user_text,cur_timestamp,' . - 'cur_comment,cur_minor_edit FROM cur WHERE ' . - 'cur_id=' . $this->getID(); - $res = wfQuery( $sql, DB_READ, 'Article::loadLastEdit' ); + $dbr =& wfGetDB( DB_READ ); + $s = $dbr->getArray( 'cur', + array( 'cur_user','cur_user_text','cur_timestamp', 'cur_comment','cur_minor_edit' ), + array( 'cur_id' => $this->getID() ), $fname ); - if ( wfNumRows( $res ) > 0 ) { - $s = wfFetchObject( $res ); + if ( $s !== false ) { $this->mUser = $s->cur_user; $this->mUserText = $s->cur_user_text; $this->mTimestamp = $s->cur_timestamp; @@ -596,29 +595,33 @@ class Article { # XXX: this is expensive; cache this info somewhere. $title = $this->mTitle; - $contribs = array(); - - $sql = 'SELECT old_user, old_user_text, ' . - ' user_real_name, MAX(old_timestamp) as timestamp' . - ' FROM old LEFT JOIN user ON old.old_user = user.user_id ' . - ' WHERE old.old_namespace = ' . $title->getNamespace() . - ' AND old.old_title = "' . $title->getDBkey() . '"' . - ' AND old.old_user != ' . $this->getUser() . - ' GROUP BY old.old_user ' . - ' ORDER BY timestamp DESC '; + $dbr = wfGetDB( DB_READ ); + $oldTable = $dbr->tableName( 'old' ); + $userTable = $dbr->tableName( 'user' ); + $encDBkey = $dbr->strencode( $title->getDBkey() ); + $ns = $title->getNamespace(); + $user = $this->getUser(); + + $sql = "SELECT old_user, old_user_text, user_real_name, MAX(old_timestamp) as timestamp + FROM $oldTable LEFT JOIN $userTable ON old_user = user_id + WHERE old_namespace = $user + AND old_title = $encDBkey + AND old_user != $user + GROUP BY old_user + ORDER BY timestamp DESC"; if ($limit > 0) { $sql .= ' LIMIT '.$limit; } - $res = wfQuery($sql, DB_READ, $fname); + $res = $dbr->query($sql, $fname); - while ( $line = wfFetchObject( $res ) ) { + while ( $line = $dbr->fetchObject( $res ) ) { $contribs[] = array($line->old_user, $line->old_user_text, $line->user_real_name); } - wfFreeResult($res); + $dbr->freeResult($res); return $contribs; } @@ -738,7 +741,7 @@ class Article { /* private */ function insertNewArticle( $text, $summary, $isminor, $watchthis ) { global $wgOut, $wgUser, $wgLinkCache, $wgMwRedir; - global $wgUseSquid, $wgDeferredUpdateList, $wgInternalServer, $wgIsPg, $wgIsMySQL; + global $wgUseSquid, $wgDeferredUpdateList, $wgInternalServer; $fname = 'Article::insertNewArticle'; @@ -754,30 +757,32 @@ class Article { $won = wfInvertTimestamp( $now ); wfSeedRandom(); $rand = number_format( mt_rand() / mt_getrandmax(), 12, '.', '' ); + $dbw =& wfGetDB( DB_WRITE ); - if ($wgIsPg) { - $cur_id_column="cur_id,"; - $cur_id=wfGetSQL(""," nextval('cur_cur_id_seq')"); - $cur_id_value="{$cur_id},"; - } else { - $cur_id_column=""; - $cur_id=""; - $cur_id_value=""; - } + $cur_id = $dbw->nextSequenceValue( 'cur_cur_id_seq' ); $isminor = ( $isminor && $wgUser->getID() ) ? 1 : 0; - $sql = "INSERT INTO cur ({$cur_id_column}cur_namespace,cur_title,cur_text," . - 'cur_comment,cur_user,cur_timestamp,cur_minor_edit,cur_counter,' . - 'cur_restrictions,cur_user_text,cur_is_redirect,' . - "cur_is_new,cur_random,cur_touched,inverse_timestamp) VALUES ({$cur_id_value}{$ns},'" . wfStrencode( $ttl ) . "', '" . - wfStrencode( $text ) . "', '" . - wfStrencode( $summary ) . "', '" . - $wgUser->getID() . "', '{$now}', " . - $isminor . ", 0, '', '" . - wfStrencode( $wgUser->getName() ) . "', $redir, 1, $rand, '{$now}', '{$won}')"; - $res = wfQuery( $sql, DB_WRITE, $fname ); - - $newid = $wgIsPg?$cur_id:wfInsertId(); + + $dbw->insertArray( 'cur', array( + 'cur_id' => $cur_id, + 'cur_namespace' => $ns, + 'cur_title' => $ttl, + 'cur_text' => $text, + 'cur_comment' => $summary, + 'cur_user' => $wgUser->getID(), + 'cur_timestamp' => $now, + 'cur_minor_edit' => $isminor, + 'cur_counter' => 0, + 'cur_restrictions' => '', + 'cur_user_text' => $wgUser->getName(), + 'cur_is_redirect' => $redir, + 'cur_is_new' => 1, + 'cur_random' => $rand, + 'cur_touched' => $now, + 'inverse_timestamp' => $won, + ), $fname ); + + $newid = $dbw->insertId(); $this->mTitle->resetArticleID( $newid ); Article::onArticleCreate( $this->mTitle ); @@ -793,8 +798,7 @@ class Article { # The talk page isn't in the regular link tables, so we need to update manually: $talkns = $ns ^ 1; # talk -> normal; normal -> talk - $sql = "UPDATE cur set cur_touched='$now' WHERE cur_namespace=$talkns AND cur_title='" . wfStrencode( $ttl ) . "'"; - wfQuery( $sql, DB_WRITE ); + $dbw->updateArray( 'cur', array( 'cur_touched' => $now ), array( 'cur_namespace' => $talkns, 'cur_title' => $ttl ), $fname ); # standard deferred updates $this->editUpdates( $text ); @@ -884,9 +888,10 @@ class Article { global $wgOut, $wgUser, $wgLinkCache; global $wgDBtransactions, $wgMwRedir; global $wgUseSquid, $wgInternalServer; - global $wgIsPg; + $fname = 'Article::updateArticle'; - + $good = true; + if ( $this->mMinorEdit ) { $me1 = 1; } else { $me1 = 0; } if ( $minor && $wgUser->getID() ) { $me2 = 1; } else { $me2 = 0; } if ( preg_match( "/^((" . $wgMwRedir->getBaseRegex() . ')[^\\n]+)/i', $text, $m ) ) { @@ -896,78 +901,87 @@ class Article { else { $redir = 0; } $text = $this->preSaveTransform( $text ); + $dbw =& wfGetDB( DB_WRITE ); # Update article, but only if changed. + # It's important that we either rollback or complete, otherwise an attacker could + # overwrite cur entries by sending precisely timed user aborts. Random bored users + # could conceivably have the same effect, especially if cur is locked for long periods. if( $wgDBtransactions ) { - $sql = 'BEGIN'; - wfQuery( $sql, DB_WRITE ); + $dbw->query( 'BEGIN', $fname ); + } else { + $userAbort = ignore_user_abort( true ); } + $oldtext = $this->getContent( true ); if ( 0 != strcmp( $text, $oldtext ) ) { $this->mCountAdjustment = $this->isCountable( $text ) - $this->isCountable( $oldtext ); - $now = wfTimestampNow(); $won = wfInvertTimestamp( $now ); - $sql = "UPDATE cur SET cur_text='" . wfStrencode( $text ) . - "',cur_comment='" . wfStrencode( $summary ) . - "',cur_minor_edit={$me2}, cur_user=" . $wgUser->getID() . - ",cur_timestamp='{$now}',cur_user_text='" . - wfStrencode( $wgUser->getName() ) . - "',cur_is_redirect={$redir}, cur_is_new=0, cur_touched='{$now}', inverse_timestamp='{$won}' " . - "WHERE cur_id=" . $this->getID() . - " AND cur_timestamp='" . $this->getTimestamp() . "'"; - $res = wfQuery( $sql, DB_WRITE, $fname ); - - if( wfAffectedRows() == 0 ) { - /* Belated edit conflict! Run away!! */ - return false; - } - # This overwrites $oldtext if revision compression is on - $flags = Article::compressRevisionText( $oldtext ); - - $oldtable=$wgIsPg?'"old"':'old'; - if ($wgIsPg) { - $oldtable='"old"'; - $old_id_column='old_id,'; - $old_id=wfGetSQL(""," nextval('old_old_id_seq')"); - $old_id_value=$old_id.','; + # First update the cur row + $dbw->updateArray( 'cur', + array( /* SET */ + 'cur_text' => $text, + 'cur_comment' => $summary, + 'cur_minor_edit' => $me2, + 'cur_user' => $wgUser->getID(), + 'cur_timestamp' => $now, + 'cur_user_text' => $wgUser->getName(), + 'cur_is_redirect' => $redir, + 'cur_is_new' => 0, + 'cur_touched' => $now, + 'inverse_timestamp' => $won + ), array( /* WHERE */ + 'cur_id' => $this->getID(), + 'cur_timestamp' => $this->getTimestamp() + ), $fname + ); + + if( $dbw->affectedRows() == 0 ) { + /* Belated edit conflict! Run away!! */ + $good = false; } else { - $oldtable='old'; - $old_id_column=''; - $old_id_value=''; + # Now insert the previous revision into old + + # This overwrites $oldtext if revision compression is on + $flags = Article::compressRevisionText( $oldtext ); + + $dbw->insertArray( 'old', + array( + 'old_id' => $dbw->nextSequenceValue( 'old_old_id_seq' ), + 'old_namespace' => $this->mTitle->getNamespace(), + 'old_title' => $this->mTitle->getDBkey(), + 'old_text' => $oldtext, + 'old_comment' => $this->getComment(), + 'old_user' => $this->getUser(), + 'old_user_text' => $this->getUserText(), + 'old_timestamp' => $this->getTimestamp(), + 'old_minor_edit' => $me1, + 'inverse_timestamp' => wfInvertTimestamp( $this->getTimestamp() ), + 'old_flags' => $flags, + ), $fname + ); + + $oldid = $dbw->insertId(); + + $bot = (int)($wgUser->isBot() || $forceBot); + RecentChange::notifyEdit( $now, $this->mTitle, $me2, $wgUser, $summary, + $oldid, $this->getTimestamp(), $bot ); + Article::onArticleEdit( $this->mTitle ); } - - $sql = "INSERT INTO $oldtable ({$old_id_column}old_namespace,old_title,old_text," . - 'old_comment,old_user,old_user_text,old_timestamp,' . - 'old_minor_edit,inverse_timestamp,old_flags) VALUES (' . - $old_id_value. - $this->mTitle->getNamespace() . ", '" . - wfStrencode( $this->mTitle->getDBkey() ) . "', '" . - wfStrencode( $oldtext ) . "', '" . - wfStrencode( $this->getComment() ) . "', " . - $this->getUser() . ", '" . - wfStrencode( $this->getUserText() ) . "', '" . - $this->getTimestamp() . "', " . $me1 . ", '" . - wfInvertTimestamp( $this->getTimestamp() ) . "','$flags')"; - $res = wfQuery( $sql, DB_WRITE, $fname ); - - $oldid = $wgIsPg?$old_id:wfInsertId( $res ); - - $bot = (int)($wgUser->isBot() || $forceBot); - RecentChange::notifyEdit( $now, $this->mTitle, $me2, $wgUser, $summary, - $oldid, $this->getTimestamp(), $bot ); - Article::onArticleEdit( $this->mTitle ); } if( $wgDBtransactions ) { - $sql = 'COMMIT'; - wfQuery( $sql, DB_WRITE ); + $dbw->query( 'COMMIT', $fname ); + } else { + ignore_user_abort( $userAbort ); } + if ( $good ) { if ($watchthis) { if (!$this->mTitle->userIsWatching()) $this->watch(); } else { @@ -986,9 +1000,9 @@ class Article { $titles = $this->mTitle->getLinksTo(); Title::touchArray( $titles ); if ( $wgUseSquid ) { - foreach ( $titles as $title ) { - $urls[] = $title->getInternalURL(); - } + foreach ( $titles as $title ) { + $urls[] = $title->getInternalURL(); + } } } @@ -1000,7 +1014,8 @@ class Article { } $this->showArticle( $text, wfMsg( 'updated' ), $sectionanchor ); - return true; + } + return $good; } # After we've either updated or inserted the article, update @@ -1093,10 +1108,15 @@ class Article { $reason = $wgRequest->getText( 'wpReasonProtect' ); if ( $confirm ) { - - $sql = "UPDATE cur SET cur_touched='" . wfTimestampNow() . "'," . - "cur_restrictions='{$limit}' WHERE cur_id={$id}"; - wfQuery( $sql, DB_WRITE, 'Article::protect' ); + $dbw =& wfGetDB( DB_WRITE ); + $dbw->updateArray( 'cur', + array( /* SET */ + 'cur_touched' => wfTimestampNow(), + 'cur_restrictions' => (string)$limit + ), array( /* WHERE */ + 'cur_id' => $id + ), 'Article::protect' + ); $log = new LogPage( wfMsg( 'protectlogpage' ), wfMsg( 'protectlogtext' ) ); if ( $limit === "" ) { @@ -1185,7 +1205,7 @@ class Article { # UI entry point for page deletion function delete() { - global $wgUser, $wgOut, $wgMessageCache, $wgRequest, $wgIsPg; + global $wgUser, $wgOut, $wgMessageCache, $wgRequest; $fname = 'Article::delete'; $confirm = $wgRequest->getBool( 'wpConfirm' ) && $wgRequest->wasPosted(); $reason = $wgRequest->getText( 'wpReason' ); @@ -1218,22 +1238,33 @@ 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_READ ); $ns = $this->mTitle->getNamespace(); $title = $this->mTitle->getDBkey(); - $etitle = wfStrencode( $title ); - $oldtable=$wgIsPg?'"old"':'old'; - $sql = "SELECT old_text,old_flags FROM $oldtable WHERE old_namespace=$ns and old_title='$etitle' ORDER BY inverse_timestamp LIMIT 1"; - $res = wfQuery( $sql, DB_READ, $fname ); - if( ($old=wfFetchObject($res)) && !$confirm ) { + $old = $dbr->getArray( 'old', + array( 'old_text', 'old_flags' ), + array( + 'old_namespace' => $ns, + 'old_title' => $title, + ), $fname, array( 'ORDER BY' => 'inverse_timestamp' ) + ); + + if( $old !== false && !$confirm ) { $skin=$wgUser->getSkin(); $wgOut->addHTML(''.wfMsg('historywarning')); $wgOut->addHTML( $skin->historyLink() .''); } - - $sql="SELECT cur_text FROM cur WHERE cur_namespace=$ns and cur_title='$etitle'"; - $res=wfQuery($sql, DB_READ, $fname); - if( ($s=wfFetchObject($res))) { - + + # Fetch cur_text + $s = $dbr->getArray( 'cur', + array( 'cur_text' ), + array( + 'cur_namespace' => $ns, + 'cur_title' => $title, + ), $fname + ); + + if( $s !== false ) { # if this is a mini-text, we can paste part of it into the deletion reason #if this is empty, an earlier revision may contain "useful" text @@ -1368,9 +1399,10 @@ class Article { $fname = 'Article::doDeleteArticle'; wfDebug( $fname."\n" ); - + + $dbw =& wfGetDB( DB_WRITE ); $ns = $this->mTitle->getNamespace(); - $t = wfStrencode( $this->mTitle->getDBkey() ); + $t = $this->mTitle->getDBkey(); $id = $this->mTitle->getArticleID(); if ( '' == $t || $id == 0 ) { @@ -1401,68 +1433,75 @@ class Article { Title::touchArray( $linksTo ); # Move article and history to the "archive" table - $sql = 'INSERT INTO archive (ar_namespace,ar_title,ar_text,' . - 'ar_comment,ar_user,ar_user_text,ar_timestamp,ar_minor_edit,' . - 'ar_flags) SELECT cur_namespace,cur_title,cur_text,cur_comment,' . - 'cur_user,cur_user_text,cur_timestamp,cur_minor_edit,0 FROM cur ' . - "WHERE cur_namespace={$ns} AND cur_title='{$t}'"; - wfQuery( $sql, DB_WRITE, $fname ); - - $sql = 'INSERT INTO archive (ar_namespace,ar_title,ar_text,' . - 'ar_comment,ar_user,ar_user_text,ar_timestamp,ar_minor_edit,' . - 'ar_flags) SELECT old_namespace,old_title,old_text,old_comment,' . - 'old_user,old_user_text,old_timestamp,old_minor_edit,old_flags ' . - "FROM old WHERE old_namespace={$ns} AND old_title='{$t}'"; - wfQuery( $sql, DB_WRITE, $fname ); - + $archiveTable = $dbw->tableName( 'archive' ); + $oldTable = $dbw->tableName( 'old' ); + $curTable = $dbw->tableName( 'cur' ); + $recentchangesTable = $dbw->tableName( 'recentchanges' ); + $linksTable = $dbw->tableName( 'links' ); + $brokenlinksTable = $dbw->tableName( 'brokenlinks' ); + + $dbw->insertSelect( 'archive', 'cur', + array( + 'ar_namespace' => 'cur_namespace', + 'ar_title' => 'cur_title', + 'ar_text' => 'cur_text', + 'ar_comment' => 'cur_comment', + 'ar_user' => 'cur_user', + 'ar_user_text' => 'cur_user_text', + 'ar_timestamp' => 'cur_timestamp', + 'ar_minor_edit' => 'cur_minor_edit', + 'ar_flags' => 0, + ), array( + 'cur_namespace' => $ns, + 'cur_title' => $t, + ), $fname + ); + + $dbw->insertSelect( 'archive', 'old', + array( + 'ar_namespace' => 'old_namespace', + 'ar_title' => 'old_title', + 'ar_text' => 'old_text', + 'ar_comment' => 'old_comment', + 'ar_user' => 'old_user', + 'ar_user_text' => 'old_user_text', + 'ar_timestamp' => 'old_timestamp', + 'ar_minor_edit' => 'old_minor_edit', + 'ar_flags' => 'old_flags' + ), array( + 'old_namespace' => $ns, + 'old_title' => $t, + ), $fname + ); + # Now that it's safely backed up, delete it - $sql = "DELETE FROM cur WHERE cur_namespace={$ns} AND " . - "cur_title='{$t}'"; - wfQuery( $sql, DB_WRITE, $fname ); - - $sql = "DELETE FROM old WHERE old_namespace={$ns} AND " . - "old_title='{$t}'"; - wfQuery( $sql, DB_WRITE, $fname ); - - $sql = "DELETE FROM recentchanges WHERE rc_namespace={$ns} AND " . - "rc_title='{$t}'"; - wfQuery( $sql, DB_WRITE, $fname ); + $dbw->delete( 'cur', array( 'cur_namespace' => $ns, 'cur_title' => $t ), $fname ); + $dbw->delete( 'old', array( 'old_namespace' => $ns, 'old_title' => $t ), $fname ); + $dbw->delete( 'recentchanges', array( 'rc_namespace' => $ns, 'rc_title' => $t ), $fname ); # Finally, clean up the link tables - $t = wfStrencode( $this->mTitle->getPrefixedDBkey() ); + $t = $this->mTitle->getPrefixedDBkey(); Article::onArticleDelete( $this->mTitle ); - $sql = 'INSERT INTO brokenlinks (bl_from,bl_to) VALUES '; - $first = true; - + # Insert broken links + $brokenLinks = array(); foreach ( $linksTo as $titleObj ) { - if ( ! $first ) { $sql .= ','; } - $first = false; # Get article ID. Efficient because it was loaded into the cache by getLinksTo(). $linkID = $titleObj->getArticleID(); - $sql .= "({$linkID},'{$t}')"; - } - if ( ! $first ) { - wfQuery( $sql, DB_WRITE, $fname ); - } - - $sql = "DELETE FROM links WHERE l_to={$id}"; - wfQuery( $sql, DB_WRITE, $fname ); - - $sql = "DELETE FROM links WHERE l_from={$id}"; - wfQuery( $sql, DB_WRITE, $fname ); - - $sql = "DELETE FROM imagelinks WHERE il_from={$id}"; - wfQuery( $sql, DB_WRITE, $fname ); - - $sql = "DELETE FROM brokenlinks WHERE bl_from={$id}"; - wfQuery( $sql, DB_WRITE, $fname ); - - $sql = "DELETE FROM categorylinks WHERE cl_from={$id}"; - wfQuery( $sql, DB_WRITE, $fname ); - + $brokenLinks[] = array( 'bl_from' => $linkID, 'bl_to' => $t ); + } + $dbw->insertArray( 'brokenlinks', $brokenLinks, $fname ); + + # Delete live links + $dbw->delete( 'links', array( 'l_to' => $id ) ); + $dbw->delete( 'links', array( 'l_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 $log = new LogPage( wfMsg( 'dellogpage' ), wfMsg( 'dellogpagetext' ) ); $art = $this->mTitle->getPrefixedText(); $log->addEntry( wfMsg( 'deletedarticle', $art ), $reason ); @@ -1475,8 +1514,9 @@ class Article { function rollback() { - global $wgUser, $wgLang, $wgOut, $wgRequest, $wgIsMySQL, $wgIsPg; - + global $wgUser, $wgLang, $wgOut, $wgRequest; + $fname = "Article::rollback"; + if ( ! $wgUser->isSysop() ) { $wgOut->sysopRequired(); return; @@ -1485,25 +1525,27 @@ class Article { $wgOut->readOnlyPage( $this->getContent( true ) ); return; } + $dbw =& wfGetDB( DB_WRITE ); # Enhanced rollback, marks edits rc_bot=1 $bot = $wgRequest->getBool( 'bot' ); # Replace all this user's current edits with the next one down - $tt = wfStrencode( $this->mTitle->getDBKey() ); + $tt = $this->mTitle->getDBKey(); $n = $this->mTitle->getNamespace(); - # Get the last editor - $sql = 'SELECT cur_id,cur_user,cur_user_text,cur_comment ' . - "FROM cur WHERE cur_title='{$tt}' AND cur_namespace={$n}"; - $res = wfQuery( $sql, DB_READ ); - if( ($x = wfNumRows( $res )) != 1 ) { + # Get the last editor, lock table exclusively + $s = $dbw->getArray( 'cur', + array( 'cur_id','cur_user','cur_user_text','cur_comment' ), + array( 'cur_title' => $tt, 'cur_namespace' => $n ), + $fname, 'FOR UPDATE' + ); + if( $s === false ) { # Something wrong $wgOut->addHTML( wfMsg( 'notanarticle' ) ); return; } - $s = wfFetchObject( $res ); - $ut = wfStrencode( $s->cur_user_text ); + $ut = $dbw->strencode( $s->cur_user_text ); $uid = $s->cur_user; $pid = $s->cur_id; @@ -1523,28 +1565,31 @@ class Article { } # Get the last edit not by this guy - - $use_index=$wgIsMySQL?"USE INDEX (name_title_timestamp)":""; - $oldtable=$wgIsPg?'"old"':'old'; - $sql = 'SELECT old_text,old_user,old_user_text,old_timestamp,old_flags ' . - "FROM $oldtable {$use_index} " . - "WHERE old_namespace={$n} AND old_title='{$tt}' " . - "AND (old_user <> {$uid} OR old_user_text <> '{$ut}') " . - 'ORDER BY inverse_timestamp LIMIT 1'; - $res = wfQuery( $sql, DB_READ ); - if( wfNumRows( $res ) != 1 ) { + $s = $dbw->getArray( 'old', + array( 'old_text','old_user','old_user_text','old_timestamp','old_flags' ), + array( + 'old_namespace' => $n, + 'old_title' => $tt, + "old_user <> {$uid} OR old_user_text <> '{$ut}'" + ), $fname, array( 'FOR UPDATE', 'USE INDEX' => 'name_title_timestamp' ) + ); + if( $s === false ) { # Something wrong $wgOut->setPageTitle(wfMsg('rollbackfailed')); $wgOut->addHTML( wfMsg( 'cantrollback' ) ); return; } - $s = wfFetchObject( $res ); if ( $bot ) { # Mark all reverted edits as bot - $sql = 'UPDATE recentchanges SET rc_bot=1 WHERE ' . - "rc_cur_id=$pid AND rc_user=$uid AND rc_timestamp > '{$s->old_timestamp}'"; - wfQuery( $sql, DB_WRITE, $fname ); + $dbw->updateArray( 'recentchanges', + array( /* SET */ + 'rc_bot' => 1 + ), array( /* WHERE */ + 'rc_user' => $uid, + "rc_timestamp > '{$s->old_timestamp}'", + ), $fname + ); } # Save it! @@ -1583,12 +1628,13 @@ class Article { { global $wgDeferredUpdateList, $wgDBname, $wgMemc; global $wgMessageCache; - + wfSeedRandom(); if ( 0 == mt_rand( 0, 999 ) ) { + $dbw =& wfGetDB( DB_WRITE ); $cutoff = wfUnix2Timestamp( time() - ( 7 * 86400 ) ); $sql = "DELETE FROM recentchanges WHERE rc_timestamp < '{$cutoff}'"; - wfQuery( $sql, DB_WRITE ); + $dbw->query( $sql ); } $id = $this->getID(); $title = $this->mTitle->getPrefixedDBkey(); @@ -1686,9 +1732,10 @@ class Article { # Loads cur_touched and returns a value indicating if it should be used function checkTouched() { $id = $this->getID(); - $sql = 'SELECT cur_touched,cur_is_redirect FROM cur WHERE cur_id='.$id; - $res = wfQuery( $sql, DB_READ, 'Article::checkTouched' ); - if( $s = wfFetchObject( $res ) ) { + $dbr =& wfGetDB( DB_READ ); + $s = $dbr->getArray( 'cur', array( 'cur_touched', 'cur_is_redirect' ), + array( 'cur_id' => $id ), $fname ); + if( $s !== false ) { $this->mTouched = $s->cur_touched; return !$s->cur_is_redirect; } else { @@ -1698,24 +1745,35 @@ class Article { # Edit an article without doing all that other stuff function quickEdit( $text, $comment = '', $minor = 0 ) { - global $wgUser, $wgMwRedir, $wgIsPg; + global $wgUser, $wgMwRedir; $fname = 'Article::quickEdit'; wfProfileIn( $fname ); + $dbw =& wfGetDB( DB_WRITE ); $ns = $this->mTitle->getNamespace(); $dbkey = $this->mTitle->getDBkey(); - $encDbKey = wfStrencode( $dbkey ); + $encDbKey = $dbw->strencode( $dbkey ); $timestamp = wfTimestampNow(); # Save to history - $oldtable=$wgIsPg?'"old"':'old'; - $sql = "INSERT INTO $oldtable (old_namespace,old_title,old_text,old_comment,old_user,old_user_text,old_timestamp,inverse_timestamp) - SELECT cur_namespace,cur_title,cur_text,cur_comment,cur_user,cur_user_text,cur_timestamp,99999999999999-cur_timestamp - FROM cur WHERE cur_namespace=$ns AND cur_title='$encDbKey'"; - wfQuery( $sql, DB_WRITE ); + $dbw->insertSelect( 'old', 'cur', + array( + 'old_namespace' => 'cur_namespace', + 'old_title' => 'cur_title', + 'old_text' => 'cur_text', + 'old_comment' => 'cur_comment', + 'old_user' => 'cur_user', + 'old_user_text' => 'cur_user_text', + 'old_timestamp' => 'cur_timestamp', + 'inverse_timestamp' => '99999999999999-cur_timestamp', + ), array( + 'cur_namespace' => $ns, + 'cur_title' => $dbkey, + ), $fname + ); # Use the affected row count to determine if the article is new - $numRows = wfAffectedRows(); + $numRows = $dbw->affectedRows(); # Make an array of fields to be inserted $fields = array( @@ -1733,14 +1791,14 @@ class Article { if ( $numRows ) { # Update article $fields['cur_is_new'] = 0; - wfUpdateArray( 'cur', $fields, array( 'cur_namespace' => $ns, 'cur_title' => $dbkey ), $fname ); + $dbw->updateArray( 'cur', $fields, array( 'cur_namespace' => $ns, 'cur_title' => $dbkey ), $fname ); } else { # Insert new article $fields['cur_is_new'] = 1; $fields['cur_namespace'] = $ns; $fields['cur_title'] = $dbkey; $fields['cur_random'] = $rand = number_format( mt_rand() / mt_getrandmax(), 12, '.', '' ); - wfInsertArray( 'cur', $fields, $fname ); + $dbw->insertArray( 'cur', $fields, $fname ); } wfProfileOut( $fname ); } @@ -1750,45 +1808,49 @@ class Article { $id = intval( $id ); global $wgHitcounterUpdateFreq; + $dbw =& wfGetDB( DB_WRITE ); + $curTable = $dbw->tableName( 'cur' ); + $hitcounterTable = $dbw->tableName( 'hitcounter' ); + $acchitsTable = $dbw->tableName( 'acchits' ); + if( $wgHitcounterUpdateFreq <= 1 ){ // - wfQuery('UPDATE cur SET cur_counter = cur_counter + 1 ' . - 'WHERE cur_id = '.$id, DB_WRITE); + $dbw->query( "UPDATE $curTable SET cur_counter = cur_counter + 1 WHERE cur_id = $id" ); return; } # Not important enough to warrant an error page in case of failure - $oldignore = wfIgnoreSQLErrors( true ); + $oldignore = $dbw->setIgnoreErrors( true ); - wfQuery("INSERT INTO hitcounter (hc_id) VALUES ({$id})", DB_WRITE); + $dbw->query( "INSERT INTO $hitcounterTable (hc_id) VALUES ({$id})" ); $checkfreq = intval( $wgHitcounterUpdateFreq/25 + 1 ); - if( (rand() % $checkfreq != 0) or (wfLastErrno() != 0) ){ + if( (rand() % $checkfreq != 0) or ($dbw->lastErrno() != 0) ){ # Most of the time (or on SQL errors), skip row count check - wfIgnoreSQLErrors( $oldignore ); + $dbw->setIgnoreErrors( $oldignore ); return; } - $res = wfQuery('SELECT COUNT(*) as n FROM hitcounter', DB_WRITE); - $row = wfFetchObject( $res ); + $res = $dbw->query("SELECT COUNT(*) as n FROM $hitcounterTable"); + $row = $dbw->fetchObject( $res ); $rown = intval( $row->n ); if( $rown >= $wgHitcounterUpdateFreq ){ wfProfileIn( 'Article::incViewCount-collect' ); $old_user_abort = ignore_user_abort( true ); - wfQuery('LOCK TABLES hitcounter WRITE', DB_WRITE); - wfQuery('CREATE TEMPORARY TABLE acchits TYPE=HEAP '. - 'SELECT hc_id,COUNT(*) AS hc_n FROM hitcounter '. - 'GROUP BY hc_id', DB_WRITE); - wfQuery('DELETE FROM hitcounter', DB_WRITE); - wfQuery('UNLOCK TABLES', DB_WRITE); - wfQuery('UPDATE cur,acchits SET cur_counter=cur_counter + hc_n '. - 'WHERE cur_id = hc_id', DB_WRITE); - wfQuery('DROP TABLE acchits', DB_WRITE); + $dbw->query("LOCK TABLES $hitcounterTable WRITE"); + $dbw->query("CREATE TEMPORARY TABLE $acchitsTable TYPE=HEAP ". + "SELECT hc_id,COUNT(*) AS hc_n FROM $hitcounterTable ". + 'GROUP BY hc_id'); + $dbw->query("DELETE FROM $hitcounterTable"); + $dbw->query('UNLOCK TABLES'); + $dbw->query("UPDATE $curTable,$acchitsTable SET cur_counter=cur_counter + hc_n ". + 'WHERE cur_id = hc_id'); + $dbw->query("DROP TABLE $acchitsTable"); ignore_user_abort( $old_user_abort ); wfProfileOut( 'Article::incViewCount-collect' ); } - wfIgnoreSQLErrors( $oldignore ); + $dbw->setIgnoreErrors( $oldignore ); } # The onArticle*() functions are supposed to be a kind of hooks diff --git a/includes/Block.php b/includes/Block.php index e5df26241a..80b102a712 100644 --- a/includes/Block.php +++ b/includes/Block.php @@ -45,31 +45,31 @@ class Block # Get a ban from the DB, with either the given address or the given username function load( $address = "", $user = 0, $killExpired = true ) { - global $wgLoadBalancer; - $fname = 'Block::load'; + $fname = 'Block::load'; + $ret = false; $killed = false; - + $dbr =& wfGetDB( DB_READ ); + $ipblocks = $dbr->tableName( 'ipblocks' ); + if ( 0 == $user && $address=="" ) { - $sql = "SELECT * from ipblocks"; + $sql = "SELECT * from $ipblocks"; } elseif ($address=="") { - $sql = "SELECT * FROM ipblocks WHERE ipb_user={$user}"; + $sql = "SELECT * FROM $ipblocks WHERE ipb_user={$user}"; } elseif ($user=="") { - $sql = "SELECT * FROM ipblocks WHERE ipb_address='" . wfStrencode( $address ) . "'"; + $sql = "SELECT * FROM $ipblocks WHERE ipb_address='" . $dbr->strencode( $address ) . "'"; } else { - $sql = "SELECT * FROM ipblocks WHERE (ipb_address='" . wfStrencode( $address ) . + $sql = "SELECT * FROM $ipblocks WHERE (ipb_address='" . $dbr->strencode( $address ) . "' OR ipb_user={$user})"; } - $wgLoadBalancer->force(-1); - $res = wfQuery( $sql, DB_READ, $fname ); - $wgLoadBalancer->force(0); - if ( 0 == wfNumRows( $res ) ) { + $res = $dbr->query( $sql, $fname ); + if ( 0 == $dbr->numRows( $res ) ) { # User is not blocked $this->clear(); } else { # Get first block - $row = wfFetchObject( $res ); + $row = $dbr->fetchObject( $res ); $this->initFromRow( $row ); if ( $killExpired ) { @@ -77,7 +77,7 @@ class Block do { $killed = $this->deleteIfExpired(); if ( $killed ) { - $row = wfFetchObject( $res ); + $row = $dbr->fetchObject( $res ); if ( $row ) { $this->initFromRow( $row ); } @@ -95,7 +95,7 @@ class Block $ret = true; } } - wfFreeResult( $res ); + $dbr->freeResult( $res ); return $ret; } @@ -132,8 +132,11 @@ class Block # Callback with a Block object for every block /*static*/ function enumBlocks( $callback, $tag, $killExpired = true ) { - $sql = 'SELECT * FROM ipblocks ORDER BY ipb_timestamp DESC'; - $res = wfQuery( $sql, DB_READ, 'Block::enumBans' ); + $dbr =& wfGetDB( DB_READ ); + $ipblocks = $dbr->tableName( 'ipblocks' ); + + $sql = "SELECT * FROM $ipblocks ORDER BY ipb_timestamp DESC"; + $res = $dbr->query( $sql, 'Block::enumBans' ); $block = new Block(); while ( $row = wfFetchObject( $res ) ) { @@ -152,24 +155,31 @@ class Block function delete() { $fname = 'Block::delete'; + $dbw =& wfGetDB( DB_WRITE ); + if ( $this->mAddress == "" ) { - $sql = "DELETE FROM ipblocks WHERE ipb_id={$this->mId}"; + $condition = array( 'ipb_id' => $this->mId ); } else { - $sql = "DELETE FROM ipblocks WHERE ipb_address='" . - wfStrencode( $this->mAddress ) . "'"; + $condition = array( 'ipb_address' => $this->mAddress ); } - wfQuery( $sql, DB_WRITE, 'Block::delete' ); - + $dbw->delete( 'ipblocks', $condition, $fname ); $this->clearCache(); } function insert() { - $sql = 'INSERT INTO ipblocks ' . - '(ipb_address, ipb_user, ipb_by, ipb_reason, ipb_timestamp, ipb_auto, ipb_expiry )' . - "VALUES ('" . wfStrencode( $this->mAddress ) . "', {$this->mUser}, {$this->mBy}, '" . - wfStrencode( $this->mReason ) . "','{$this->mTimestamp}', {$this->mAuto}, '{$this->mExpiry}')"; - wfQuery( $sql, DB_WRITE, 'Block::insert' ); + $dbw =& wfGetDB( DB_WRITE ); + $dbw->insertArray( 'ipblocks', + array( + 'ipb_address' => $this->mAddress, + 'ipb_user' => $this->mUser, + 'ipb_by' => $this->mBy, + 'ipb_reason' => $this->mReason, + 'ipb_timestamp' => $this->mTimestamp, + 'ipb_auto' => $this->mAuto, + 'ipb_expiry' => $this->mExpiry, + ), 'Block::insert' + ); $this->clearCache(); } @@ -204,10 +214,15 @@ class Block $this->mTimestamp = wfTimestampNow(); $this->mExpiry = Block::getAutoblockExpiry( $this->mTimestamp ); - wfQuery( 'UPDATE ipblocks SET ' . - "ipb_timestamp='" . $this->mTimestamp . "', " . - "ipb_expiry='" . $this->mExpiry . "' " . - "WHERE ipb_address='" . wfStrencode( $this->mAddress ) . "'", DB_WRITE, 'Block::updateTimestamp' ); + $dbw =& wfGetDB( DB_WRITE ); + $dbw->updateArray( 'ipblocks', + array( /* SET */ + 'ipb_timestamp' => $this->mTimestamp, + 'ipb_expiry' => $this->mExpiry, + ), array( /* WHERE */ + 'ipb_address' => $this->mAddress + ), 'Block::updateTimestamp' + ); $this->clearCache(); } diff --git a/includes/Database.php b/includes/Database.php index bab6cd8735..c1ba5cc94c 100644 --- a/includes/Database.php +++ b/includes/Database.php @@ -60,7 +60,8 @@ class Database { # Other functions #------------------------------------------------------------------------------ - function Database() + function Database( $server = false, $user = false, $password = false, $dbName = false, + $failFunction = false, $debug = false, $bufferResults = true, $ignoreErrors = false ) { global $wgOut; # Can't get a reference if it hasn't been set yet @@ -68,27 +69,27 @@ class Database { $wgOut = NULL; } $this->mOut =& $wgOut; - + + $this->mFailFunction = $failFunction; + $this->mIgnoreErrors = $ignoreErrors; + $this->mDebug = $debug; + $this->mBufferResults = $bufferResults; + if ( $server ) { + $this->open( $server, $user, $password, $dbName ); + } } /* static */ function newFromParams( $server, $user, $password, $dbName, $failFunction = false, $debug = false, $bufferResults = true, $ignoreErrors = false ) { - $db = new Database; - $db->mFailFunction = $failFunction; - $db->mIgnoreErrors = $ignoreErrors; - $db->mDebug = $debug; - $db->mBufferResults = $bufferResults; - $db->open( $server, $user, $password, $dbName ); - return $db; + return new Database( $server, $user, $password, $dbName, $failFunction, $debug, + $bufferResults, $ignoreErrors ); } # Usually aborts on failure # If the failFunction is set to a non-zero integer, returns success function open( $server, $user, $password, $dbName ) { - global $wgEmergencyContact; - # Test for missing mysql.so # Otherwise we get a suppressed fatal error, which is very hard to track down if ( !function_exists( 'mysql_connect' ) ) { @@ -155,7 +156,7 @@ class Database { # Usually aborts on failure # If errors are explicitly ignored, returns success - function query( $sql, $fname = "" ) + function query( $sql, $fname = "", $tempIgnore = false ) { global $wgProfiling, $wgCommandLineMode; @@ -173,16 +174,26 @@ class Database { $sqlx = wordwrap(strtr($sqlx,"\t\n"," ")); wfDebug( "SQL: $sqlx\n" ); } + # Add a comment for easy SHOW PROCESSLIST interpretation + if ( $fname ) { + $commentedSql = "/* $fname */ $sql"; + } else { + $commentedSql = $sql; + } + if( $this->mBufferResults ) { - $ret = mysql_query( $sql, $this->mConn ); + $ret = mysql_query( $commentedSql, $this->mConn ); } else { - $ret = mysql_unbuffered_query( $sql, $this->mConn ); + $ret = mysql_unbuffered_query( $commentedSql, $this->mConn ); } if ( false === $ret ) { + # Ignore errors during error handling to avoid infinite recursion + $ignore = $this->setIgnoreErrors( true ); + $error = mysql_error( $this->mConn ); $errno = mysql_errno( $this->mConn ); - if( $this->mIgnoreErrors ) { + if( $ignore || $tempIgnore ) { wfDebug("SQL ERROR (ignored): " . $error . "\n"); } else { $sql1line = str_replace( "\n", "\\n", $sql ); @@ -199,6 +210,7 @@ class Database { $this->mOut->databaseError( $fname, $sql, $error, $errno ); } } + $this->setIgnoreErrors( $ignore ); } if ( $wgProfiling ) { @@ -216,7 +228,7 @@ class Database { @$row = mysql_fetch_object( $res ); # FIXME: HACK HACK HACK HACK debug if( mysql_errno() ) { - wfDebugDieBacktrace( "SQL error: " . htmlspecialchars( mysql_error() ) ); + wfDebugDieBacktrace( "Error in fetchObject(): " . htmlspecialchars( mysql_error() ) ); } return $row; } @@ -224,7 +236,7 @@ class Database { function fetchRow( $res ) { @$row = mysql_fetch_array( $res ); if (mysql_errno() ) { - wfDebugDieBacktrace( "SQL error: " . htmlspecialchars( mysql_error() ) ); + wfDebugDieBacktrace( "Error in fetchRow(): " . htmlspecialchars( mysql_error() ) ); } return $row; } @@ -232,7 +244,7 @@ class Database { function numRows( $res ) { @$n = mysql_num_rows( $res ); if( mysql_errno() ) { - wfDebugDieBacktrace( "SQL error: " . htmlspecialchars( mysql_error() ) ); + wfDebugDieBacktrace( "Error in numRows(): " . htmlspecialchars( mysql_error() ) ); } return $n; } @@ -249,6 +261,7 @@ class Database { # If errors are explicitly ignored, returns success function set( $table, $var, $value, $cond, $fname = "Database::set" ) { + $table = $this->tableName( $table ); $sql = "UPDATE $table SET $var = '" . wfStrencode( $value ) . "' WHERE ($cond)"; return !!$this->query( $sql, DB_WRITE, $fname ); @@ -257,41 +270,89 @@ class Database { # Simple SELECT wrapper, returns a single field, input must be encoded # Usually aborts on failure # If errors are explicitly ignored, returns FALSE on failure - function get( $table, $var, $cond, $fname = "Database::get" ) + function getField( $table, $var, $cond, $fname = "Database::get" ) { - $sql = "SELECT $var FROM $table WHERE ($cond)"; - $result = $this->query( $sql, DB_READ, $fname ); - - $ret = ""; - if ( mysql_num_rows( $result ) > 0 ) { - $s = mysql_fetch_object( $result ); - $ret = $s->$var; - mysql_free_result( $result ); + $table = $this->tableName( $table ); + $from = $table?" FROM $table ":""; + if ( is_array( $cond ) ) { + $where = ' WHERE ' . $this->makeList( $cond, LIST_AND ); + } elseif ( $cond ) { + $where = " WHERE ($cond)"; + } else { + $where = ''; + } + $sql = "SELECT $var $from $where LIMIT 1"; + $result = $this->query( $sql, $fname ); + + $ret = false; + if ( $this->numRows( $result ) > 0 ) { + $s = $this->fetchRow( $result ); + $ret = $s[0]; + $this->freeResult( $result ); } return $ret; } - # More complex SELECT wrapper, single row only - # Aborts or returns FALSE on error - # Takes an array of selected variables, and a condition map, which is ANDed - # e.g. getArray( "cur", array( "cur_id" ), array( "cur_namespace" => 0, "cur_title" => "Astronomy" ) ) - # would return an object where $obj->cur_id is the ID of the Astronomy article - function getArray( $table, $vars, $conds, $fname = "Database::getArray" ) + # SELECT wrapper + function select( $table, $vars, $conds, $fname = "Database::select", $options = array() ) { $vars = implode( ",", $vars ); + $table = $this->tableName( $table ); + if ( !is_array( $options ) ) { + $options = array( $options ); + } + + $tailOpts = ''; + + if ( isset( $options['ORDER BY'] ) ) { + $tailOpts .= " ORDER BY {$options['ORDER BY']}"; + } + if ( isset( $options['LIMIT'] ) ) { + $tailOpts .= " LIMIT {$options['LIMIT']}"; + } + + if ( is_numeric( array_search( 'FOR UPDATE', $options ) ) ) { + $tailOpts .= ' FOR UPDATE'; + } + + if ( is_numeric( array_search( 'LOCK IN SHARE MODE', $options ) ) ) { + $tailOpts .= ' LOCK IN SHARE MODE'; + } + + if ( isset( $options['USE INDEX'] ) ) { + $useIndex = $this->useIndexClause( $options['USE INDEX'] ); + } else { + $useIndex = ''; + } + if ( $conds !== false ) { - $where = Database::makeList( $conds, LIST_AND ); - $sql = "SELECT $vars FROM $table WHERE $where LIMIT 1"; + $where = $this->makeList( $conds, LIST_AND ); + $sql = "SELECT $vars FROM $table $useIndex WHERE $where $tailOpts"; } else { - $sql = "SELECT $vars FROM $table LIMIT 1"; + $sql = "SELECT $vars FROM $table $useIndex $tailOpts"; } - $res = $this->query( $sql, $fname ); + return $this->query( $sql, $fname ); + } + + # Single row SELECT wrapper + # Aborts or returns FALSE on error + # + # $vars: the selected variables + # $conds: a condition map, terms are ANDed together. + # Items with numeric keys are taken to be literal conditions + # Takes an array of selected variables, and a condition map, which is ANDed + # e.g. getArray( "cur", array( "cur_id" ), array( "cur_namespace" => 0, "cur_title" => "Astronomy" ) ) + # would return an object where $obj->cur_id is the ID of the Astronomy article + function getArray( $table, $vars, $conds, $fname = "Database::getArray", $options = array() ) { + $options['LIMIT'] = 1; + $res = $this->select( $table, $vars, $conds, $fname, $options ); if ( $res === false || !$this->numRows( $res ) ) { return false; } $obj = $this->fetchObject( $res ); $this->freeResult( $res ); return $obj; + } # Removes most variables from an SQL query and replaces them with X or N for numbers. @@ -322,6 +383,7 @@ class Database { # If errors are explicitly ignored, returns NULL on failure function fieldExists( $table, $field, $fname = "Database::fieldExists" ) { + $table = $this->tableName( $table ); $res = $this->query( "DESCRIBE $table", DB_READ, $fname ); if ( !$res ) { return NULL; @@ -343,28 +405,35 @@ class Database { # If errors are explicitly ignored, returns NULL on failure function indexExists( $table, $index, $fname = "Database::indexExists" ) { + $info = $this->indexInfo( $table, $index, $fname ); + if ( is_null( $info ) ) { + return NULL; + } else { + return $info !== false; + } + } + + function indexInfo( $table, $index, $fname = "Database::indexInfo" ) { # SHOW INDEX works in MySQL 3.23.58, but SHOW INDEXES does not. # SHOW INDEX should work for 3.x and up: # http://dev.mysql.com/doc/mysql/en/SHOW_INDEX.html + $table = $this->tableName( $table ); $sql = "SHOW INDEX FROM $table"; - $res = $this->query( $sql, DB_READ, $fname ); + $res = $this->query( $sql, $fname ); if ( !$res ) { return NULL; } - $found = false; - while ( $row = $this->fetchObject( $res ) ) { if ( $row->Key_name == $index ) { - $found = true; - break; + return $row; } } - return $found; + return false; } - function tableExists( $table ) { + $table = $this->tableName( $table ); $old = $this->mIgnoreErrors; $this->mIgnoreErrors = true; $res = $this->query( "SELECT 1 FROM $table LIMIT 1" ); @@ -379,6 +448,7 @@ class Database { function fieldInfo( $table, $field ) { + $table = $this->tableName( $table ); $res = $this->query( "SELECT * FROM $table LIMIT 1" ); $n = mysql_num_fields( $res ); for( $i = 0; $i < $n; $i++ ) { @@ -391,39 +461,61 @@ class Database { } # INSERT wrapper, inserts an array into a table - # Keys are field names, values are values + # + # $a may be a single associative array, or an array of these with numeric keys, for + # multi-row insert. + # # Usually aborts on failure # If errors are explicitly ignored, returns success - function insertArray( $table, $a, $fname = "Database::insertArray" ) + function insertArray( $table, $a, $fname = "Database::insertArray", $options = array() ) { - $sql1 = "INSERT INTO $table ("; - $sql2 = "VALUES (" . Database::makeList( $a ); - $first = true; - foreach ( $a as $field => $value ) { - if ( !$first ) { - $sql1 .= ","; + $table = $this->tableName( $table ); + if ( isset( $a[0] ) && is_array( $a[0] ) ) { + $multi = true; + $keys = array_keys( $a[0] ); + } else { + $multi = false; + $keys = array_keys( $a ); + } + + $sql = 'INSERT ' . implode( ' ', $options ) . + " INTO $table (" . implode( ',', $keys ) . ') VALUES '; + + if ( $multi ) { + $first = true; + foreach ( $a as $row ) { + if ( $first ) { + $first = false; + } else { + $sql .= ","; + } + $sql .= '(' . $this->makeList( $row ) . ')'; } - $first = false; - $sql1 .= $field; + } else { + $sql .= '(' . $this->makeList( $a ) . ')'; } - $sql = "$sql1) $sql2)"; return !!$this->query( $sql, $fname ); } - # A cross between insertArray and getArray, takes a condition array and a SET array + # UPDATE wrapper, takes a condition array and a SET array function updateArray( $table, $values, $conds, $fname = "Database::updateArray" ) { + $table = $this->tableName( $table ); $sql = "UPDATE $table SET " . $this->makeList( $values, LIST_SET ); $sql .= " WHERE " . $this->makeList( $conds, LIST_AND ); $this->query( $sql, $fname ); } # Makes a wfStrencoded list from an array - # $mode: LIST_COMMA - comma separated, no field names - # LIST_AND - ANDed WHERE clause (without the WHERE) - # LIST_SET - comma separated with field names, like a SET clause - /* static */ function makeList( $a, $mode = LIST_COMMA ) + # $mode: LIST_COMMA - comma separated, no field names + # LIST_AND - ANDed WHERE clause (without the WHERE) + # LIST_SET - comma separated with field names, like a SET clause + function makeList( $a, $mode = LIST_COMMA ) { + if ( !is_array( $a ) ) { + wfDebugDieBacktrace( 'Database::makeList called with incorrect parameters' ); + } + $first = true; $list = ""; foreach ( $a as $field => $value ) { @@ -436,13 +528,13 @@ class Database { } else { $first = false; } - if ( $mode == LIST_AND || $mode == LIST_SET ) { - $list .= "$field="; - } - if ( !is_numeric( $value ) ) { - $list .= "'" . wfStrencode( $value ) . "'"; + if ( $mode == LIST_AND && is_numeric( $field ) ) { + $list .= "($value)"; } else { - $list .= $value; + if ( $mode == LIST_AND || $mode == LIST_SET ) { + $list .= "$field="; + } + $list .= $this->addQuotes( $value ); } } return $list; @@ -465,8 +557,151 @@ class Database { function stopTimer() { } + + function tableName( $name ) { + return $name; + } + + function strencode( $s ) { + return addslashes( $s ); + } + + # If it's a string, adds quotes and backslashes + # Otherwise returns as-is + function addQuotes( $s ) { + if ( !is_numeric( $s ) ) { + $s = "'" . $this->strencode( $s ) . "'"; + } else if ( is_null( $s ) ) { + $s = 'NULL'; + } + return $s; + } + + # Returns an appropriately quoted sequence value for inserting a new row. + # MySQL has autoincrement fields, so this is just NULL. But the PostgreSQL + # subclass will return an integer, and save the value for insertId() + function nextSequenceValue( $seqName ) { + return NULL; + } + + # USE INDEX clause + # PostgreSQL doesn't have them and returns "" + function useIndexClause( $index ) { + return "USE INDEX ($index)"; + } + + # REPLACE query wrapper + # PostgreSQL simulates this with a DELETE followed by INSERT + # $row is the row to insert, an associative array + # $uniqueIndexes is an array of indexes. Each element may be either a + # field name or an array of field names + # + # It may be more efficient to leave off unique indexes which are unlikely to collide. + # However if you do this, you run the risk of encountering errors which wouldn't have + # occurred in MySQL + function replace( $table, $uniqueIndexes, $rows, $fname = "Database::replace" ) { + $table = $this->tableName( $table ); + + # Single row case + if ( !is_array( reset( $rows ) ) ) { + $rows = array( $rows ); + } + + $sql = "REPLACE INTO $table (" . implode( ',', array_flip( $rows[0] ) ) .") VALUES "; + $first = true; + foreach ( $rows as $row ) { + if ( $first ) { + $first = false; + } else { + $sql .= ","; + } + $sql .= "(" . $this->makeList( $row ) . ")"; + } + return $this->query( $sql, $fname ); + } + + # DELETE where the condition is a join + # MySQL does this with a multi-table DELETE syntax, PostgreSQL does it with sub-selects + # + # $delTable is the table to delete from + # $joinTable is the other table + # $delVar is the variable to join on, in the first table + # $joinVar is the variable to join on, in the second table + # $conds is a condition array of field names mapped to variables, ANDed together in the WHERE clause + # + # For safety, an empty $conds will not delete everything. If you want to delete all rows where the + # join condition matches, set $conds='*' + # + # DO NOT put the join condition in $conds + function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = "Database::deleteJoin" ) { + if ( !$conds ) { + wfDebugDieBacktrace( 'Database::deleteJoin() called with empty $conds' ); + } + + $delTable = $this->tableName( $delTable ); + $joinTable = $this->tableName( $joinTable ); + $sql = "DELETE $delTable FROM $delTable, $joinTable WHERE $delVar=$joinVar "; + if ( $conds != '*' ) { + $sql .= " AND " . $this->makeList( $conds, LIST_AND ); + } + + return $this->query( $sql, $fname ); + } + + # Returns the size of a text field, or -1 for "unlimited" + function textFieldSize( $table, $field ) { + $table = $this->tableName( $table ); + $sql = "SHOW COLUMNS FROM $table LIKE \"$field\";"; + $res = $this->query( $sql, "Database::textFieldSize" ); + $row = wfFetchObject( $res ); + $this->freeResult( $res ); + + if ( preg_match( "/\((.*)\)/", $row->Type, $m ) ) { + $size = $m[1]; + } else { + $size = -1; + } + return $size; + } + + function lowPriorityOption() { + return 'LOW_PRIORITY'; + } + + # Use $conds == "*" to delete all rows + function delete( $table, $conds, $fname = "Database::delete" ) { + if ( !$conds ) { + wfDebugDieBacktrace( "Database::delete() called with no conditions" ); + } + $table = $this->tableName( $table ); + $sql = "DELETE FROM $table "; + if ( $conds != '*' ) { + $sql .= "WHERE " . $this->makeList( $conds, LIST_AND ); + } + return $this->query( $sql, $fname ); + } + + # INSERT SELECT wrapper + # $varMap must be an associative array of the form array( 'dest1' => 'source1', ...) + # Source items may be literals rather than field names, but strings should be quoted with Database::addQuotes() + # $conds may be "*" to copy the whole table + function insertSelect( $destTable, $srcTable, $varMap, $conds, $fname = 'Database::insertSelect' ) { + $destTable = $this->tableName( $destTable ); + $srcTable = $this->tableName( $srcTable ); + $sql = "INSERT INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ")" . + " SELECT " . implode( ',', $varMap ) . + " FROM $srcTable"; + if ( $conds != '*' ) { + $sql .= " WHERE " . $this->makeList( $conds, LIST_AND ); + } + return $this->query( $sql, $fname ); + } } +class DatabaseMysql extends Database { + # Inherit all +} + #------------------------------------------------------------------------------ # Global functions #------------------------------------------------------------------------------ @@ -520,11 +755,6 @@ function wfEmergencyAbort( &$conn, $error ) { wfAbruptExit(); } -function wfStrencode( $s ) -{ - return addslashes( $s ); -} - function wfLimitResult( $limit, $offset ) { return " LIMIT ".(is_numeric($offset)?"{$offset},":"")."{$limit} "; } diff --git a/includes/DatabaseFunctions.php b/includes/DatabaseFunctions.php index 3fde31b9f0..c5f1b6dbe6 100644 --- a/includes/DatabaseFunctions.php +++ b/includes/DatabaseFunctions.php @@ -10,18 +10,6 @@ # Note: $wgDatabase has ceased to exist. Destroy all references. -$wgIsMySQL=false; -$wgIsPg=false; - -if ($wgDBtype=="mysql") { - require_once( "Database.php" ); - $wgIsMySQL=true; -} elseif ($wgDBtype=="pgsql") { - require_once( "DatabasePostgreSQL.php" ); - $wgIsPg=true; -} - - # Usually aborts on failure # If errors are explicitly ignored, returns success function wfQuery( $sql, $db, $fname = "" ) @@ -220,7 +208,7 @@ function wfGetSQL( $table, $var, $cond="", $dbi = DB_LAST ) { $db =& wfGetDB( $dbi ); if ( $db !== false ) { - return $db->get( $table, $var, $cond ); + return $db->getField( $table, $var, $cond ); } else { return false; } @@ -277,4 +265,40 @@ function wfUpdateArray( $table, $values, $conds, $fname = "wfUpdateArray", $dbi } } +function wfTableName( $name, $dbi = DB_LAST ) { + $db =& wfGetDB( $dbi ); + if ( $db !== false ) { + return $db->tableName( $name ); + } else { + return false; + } +} + +function wfStrencode( $s, $dbi = DB_LAST ) +{ + $db =& wfGetDB( $dbi ); + if ( $db !== false ) { + return $db->strencode( $s ); + } else { + return false; + } +} + +function wfNextSequenceValue( $seqName, $dbi = DB_WRITE ) { + $db =& wfGetDB( $dbi ); + if ( $db !== false ) { + return $db->nextSequenceValue( $seqName ); + } else { + return false; + } +} + +function wfUseIndexClause( $index, $dbi = DB_READ ) { + $db =& wfGetDB( $dbi ); + if ( $db !== false ) { + return $db->useIndexClause( $index ); + } else { + return false; + } +} ?> diff --git a/includes/DatabasePostgreSQL.php b/includes/DatabasePostgreSQL.php index 2b3ba25f10..1b22a8e0f5 100644 --- a/includes/DatabasePostgreSQL.php +++ b/includes/DatabasePostgreSQL.php @@ -14,98 +14,32 @@ # # Hashar -require_once( "FulltextStoplist.php" ); -require_once( "CacheManager.php" ); +class DatabasePgsql extends Database { + var $mInsertId = NULL; -define( "DB_READ", -1 ); -define( "DB_WRITE", -2 ); -define( "DB_LAST", -3 ); - -define( "LIST_COMMA", 0 ); -define( "LIST_AND", 1 ); -define( "LIST_SET", 2 ); - -class Database { - -#------------------------------------------------------------------------------ -# Variables -#------------------------------------------------------------------------------ - /* private */ var $mLastQuery = ""; - /* private */ var $mBufferResults = true; - /* private */ var $mIgnoreErrors = false; - - /* private */ var $mServer, $mUser, $mPassword, $mConn, $mDBname; - /* private */ var $mOut, $mDebug, $mOpened = false; - - /* private */ var $mFailFunction; - /* private */ var $mLastResult; - -#------------------------------------------------------------------------------ -# Accessors -#------------------------------------------------------------------------------ - # Set functions - # These set a variable and return the previous state - - # Fail function, takes a Database as a parameter - # Set to false for default, 1 for ignore errors - function setFailFunction( $function ) { return wfSetVar( $this->mFailFunction, $function ); } - - # Output page, used for reporting errors - # FALSE means discard output - function &setOutputPage( &$out ) { $this->mOut =& $out; } - - # Boolean, controls output of large amounts of debug information - function setDebug( $debug ) { return wfSetVar( $this->mDebug, $debug ); } - - # Turns buffering of SQL result sets on (true) or off (false). Default is - # "on" and it should not be changed without good reasons. - function setBufferResults( $buffer ) { return wfSetVar( $this->mBufferResults, $buffer ); } - - # Turns on (false) or off (true) the automatic generation and sending - # of a "we're sorry, but there has been a database error" page on - # database errors. Default is on (false). When turned off, the - # code should use wfLastErrno() and wfLastError() to handle the - # situation as appropriate. - function setIgnoreErrors( $ignoreErrors ) { return wfSetVar( $this->mIgnoreErrors, $ignoreErrors ); } - - # Get functions - - function lastQuery() { return $this->mLastQuery; } - function isOpen() { return $this->mOpened; } - -#------------------------------------------------------------------------------ -# Other functions -#------------------------------------------------------------------------------ - - function Database() + function DatabasePgsql($server = false, $user = false, $password = false, $dbName = false, + $failFunction = false, $debug = false, $bufferResults = true, $ignoreErrors = false) { - global $wgOut; - # Can't get a reference if it hasn't been set yet - if ( !isset( $wgOut ) ) { - $wgOut = NULL; - } - $this->mOut =& $wgOut; - + Database::Database( $server, $user, $password, $dbName, $failFunction, $debug, + $bufferResults, $ignoreErrors ); } - + /* static */ function newFromParams( $server, $user, $password, $dbName, $failFunction = false, $debug = false, $bufferResults = true, $ignoreErrors = false ) { - $db = new Database; - $db->mFailFunction = $failFunction; - $db->mIgnoreErrors = $ignoreErrors; - $db->mDebug = $debug; - $db->mBufferResults = $bufferResults; - $db->open( $server, $user, $password, $dbName ); - return $db; + return new DatabasePgsql( $server, $user, $password, $dbName, $failFunction, $debug, + $bufferResults, $ignoreErrors ); } - + # Usually aborts on failure # If the failFunction is set to a non-zero integer, returns success function open( $server, $user, $password, $dbName ) { - global $wgEmergencyContact; - + # Test for PostgreSQL support, to avoid suppressed fatal error + if ( !function_exists( 'pg_connect' ) ) { + die( "PostgreSQL functions missing, have you compiled PHP with the --with-pgsql option?\n" ); + } + $this->close(); $this->mServer = $server; $this->mUser = $user; @@ -114,7 +48,6 @@ class Database { $success = false; - if ( "" != $dbName ) { # start a database connection @$this->mConn = pg_connect("host=$server dbname=$dbName user=$user password=$password"); @@ -141,20 +74,9 @@ class Database { } } - /* private */ function reportConnectionError( $msg = "") - { - if ( $this->mFailFunction ) { - if ( !is_int( $this->mFailFunction ) ) { - $this->$mFailFunction( $this ); - } - } else { - wfEmergencyAbort( $this ); - } - } - # Usually aborts on failure # If errors are explicitly ignored, returns success - function query( $sql, $fname = "" ) + function query( $sql, $fname = "", $tempIgnore = false ) { global $wgProfiling; @@ -176,10 +98,12 @@ class Database { $ret = pg_query( $this->mConn , $sql); $this->mLastResult = $ret; if ( false == $ret ) { + // Ignore errors during error handling to prevent infinite recursion + $ignore = $this->setIgnoreErrors( true ); $error = pg_last_error( $this->mConn ); // TODO FIXME : no error number function in postgre // $errno = mysql_errno( $this->mConn ); - if( $this->mIgnoreErrors ) { + if( $ignore || $tempIgnore ) { wfDebug("SQL ERROR (ignored): " . $error . "\n"); } else { wfDebug("SQL ERROR: " . $error . "\n"); @@ -188,6 +112,7 @@ class Database { $this->mOut->databaseError( $fname, $sql, $error, 0 ); } } + $this->setIgnoreErrors( $ignore ); } if ( $wgProfiling ) { @@ -196,6 +121,10 @@ class Database { return $ret; } + function queryIgnore( $sql, $fname = "" ) { + return $this->query( $sql, $fname, true ); + } + function freeResult( $res ) { if ( !@pg_free_result( $res ) ) { wfDebugDieBacktrace( "Unable to free PostgreSQL result\n" ); @@ -231,150 +160,40 @@ class Database { } function numFields( $res ) { return pg_num_fields( $res ); } function fieldName( $res, $n ) { return pg_field_name( $res, $n ); } - // TODO FIXME: need to implement something here + + # This must be called after nextSequenceVal function insertId() { - //return mysql_insert_id( $this->mConn ); - wfDebugDieBacktrace( "Database::insertId() error : not implemented for postgre, use sequences" ); + return $this->mInsertId; } + function dataSeek( $res, $row ) { return pg_result_seek( $res, $row ); } - function lastErrno() { return $this->lastError(); } function lastError() { return pg_last_error(); } function affectedRows() { return pg_affected_rows( $this->mLastResult ); } - # Simple UPDATE wrapper - # Usually aborts on failure - # If errors are explicitly ignored, returns success - function set( $table, $var, $value, $cond, $fname = "Database::set" ) - { - $sql = "UPDATE \"$table\" SET \"$var\" = '" . - wfStrencode( $value ) . "' WHERE ($cond)"; - return !!$this->query( $sql, DB_WRITE, $fname ); - } - - # Simple SELECT wrapper, returns a single field, input must be encoded - # Usually aborts on failure - # If errors are explicitly ignored, returns FALSE on failure - function get( $table, $var, $cond, $fname = "Database::get" ) - { - $from=$table?" FROM \"$table\" ":""; - $where=$cond?" WHERE ($cond)":""; - - $sql = "SELECT $var $from $where"; - - $result = $this->query( $sql, DB_READ, $fname ); - - $ret = ""; - if ( pg_num_rows( $result ) > 0 ) { - $s = pg_fetch_array( $result ); - $ret = $s[0]; - pg_free_result( $result ); - } - return $ret; - } - - # More complex SELECT wrapper, single row only - # Aborts or returns FALSE on error - # Takes an array of selected variables, and a condition map, which is ANDed - # e.g. getArray( "cur", array( "cur_id" ), array( "cur_namespace" => 0, "cur_title" => "Astronomy" ) ) - # would return an object where $obj->cur_id is the ID of the Astronomy article - function getArray( $table, $vars, $conds, $fname = "Database::getArray" ) - { - $vars = implode( ",", $vars ); - $where = Database::makeList( $conds, LIST_AND ); - $sql = "SELECT \"$vars\" FROM \"$table\" WHERE $where LIMIT 1"; - $res = $this->query( $sql, $fname ); - if ( $res === false || !$this->numRows( $res ) ) { - return false; - } - $obj = $this->fetchObject( $res ); - $this->freeResult( $res ); - return $obj; - } - - # Removes most variables from an SQL query and replaces them with X or N for numbers. - # It's only slightly flawed. Don't use for anything important. - /* static */ function generalizeSQL( $sql ) - { - # This does the same as the regexp below would do, but in such a way - # as to avoid crashing php on some large strings. - # $sql = preg_replace ( "/'([^\\\\']|\\\\.)*'|\"([^\\\\\"]|\\\\.)*\"/", "'X'", $sql); - - $sql = str_replace ( "\\\\", "", $sql); - $sql = str_replace ( "\\'", "", $sql); - $sql = str_replace ( "\\\"", "", $sql); - $sql = preg_replace ("/'.*'/s", "'X'", $sql); - $sql = preg_replace ('/".*"/s', "'X'", $sql); - - # All newlines, tabs, etc replaced by single space - $sql = preg_replace ( "/\s+/", " ", $sql); - - # All numbers => N - $sql = preg_replace ('/-?[0-9]+/s', "N", $sql); - - return $sql; - } - - # Determines whether a field exists in a table - # Usually aborts on failure - # If errors are explicitly ignored, returns NULL on failure - function fieldExists( $table, $field, $fname = "Database::fieldExists" ) - { - $res = $this->query( "DESCRIBE '$table'", DB_READ, $fname ); - if ( !$res ) { - return NULL; - } - - $found = false; - - while ( $row = $this->fetchObject( $res ) ) { - if ( $row->Field == $field ) { - $found = true; - break; - } - } - return $found; - } - - # Determines whether an index exists - # Usually aborts on failure + # Returns information about an index # If errors are explicitly ignored, returns NULL on failure - function indexExists( $table, $index, $fname = "Database::indexExists" ) + function indexInfo( $table, $index, $fname = "Database::indexExists" ) { $sql = "SELECT indexname FROM pg_indexes WHERE tablename='$table'"; - $res = $this->query( $sql, DB_READ, $fname ); + $res = $this->query( $sql, $fname ); if ( !$res ) { return NULL; } - $found = false; - while ( $row = $this->fetchObject( $res ) ) { if ( $row->Key_name == $index ) { - $found = true; - break; + return $row; } } - return $found; - } - - function tableExists( $table ) - { - $old = $this->mIgnoreErrors; - $this->mIgnoreErrors = true; - $res = $this->query( "SELECT 1 FROM '$table' LIMIT 1" ); - $this->mIgnoreErrors = $old; - if( $res ) { - $this->freeResult( $res ); - return true; - } else { - return false; - } + return false; } function fieldInfo( $table, $field ) { + wfDebugDieBacktrace( "Database::fieldInfo() error : mysql_fetch_field() not implemented for postgre" ); + /* $res = $this->query( "SELECT * FROM '$table' LIMIT 1" ); $n = pg_num_fields( $res ); for( $i = 0; $i < $n; $i++ ) { @@ -385,134 +204,154 @@ class Database { return $meta; } } - return false; + return false;*/ } - # INSERT wrapper, inserts an array into a table - # Keys are field names, values are values - # Usually aborts on failure - # If errors are explicitly ignored, returns success - function insertArray( $table, $a, $fname = "Database::insertArray" ) - { - $sql1 = "INSERT INTO \"$table\" ("; - $sql2 = "VALUES (" . Database::makeList( $a ); - $first = true; - foreach ( $a as $field => $value ) { - if ( !$first ) { - $sql1 .= ","; - } - $first = false; - $sql1 .= $field; + function insertArray( $table, $a, $fname = "Database::insertArray", $options = array() ) { + # PostgreSQL doesn't support options + # We have a go at faking some of them + if ( in_array( 'IGNORE', $options ) ) { + $ignore = true; + $oldIgnore = $this->setIgnoreErrors( true ); } - $sql = "$sql1) $sql2)"; - return !!$this->query( $sql, $fname ); - } - - # A cross between insertArray and getArray, takes a condition array and a SET array - function updateArray( $table, $values, $conds, $fname = "Database::updateArray" ) - { - $sql = "UPDATE '$table' SET " . $this->makeList( $values, LIST_SET ); - $sql .= " WHERE " . $this->makeList( $conds, LIST_AND ); - $this->query( $sql, $fname ); - } - - # Makes a wfStrencoded list from an array - # $mode: LIST_COMMA - comma separated, no field names - # LIST_AND - ANDed WHERE clause (without the WHERE) - # LIST_SET - comma separated with field names, like a SET clause - /* static */ function makeList( $a, $mode = LIST_COMMA ) - { - $first = true; - $list = ""; - foreach ( $a as $field => $value ) { - if ( !$first ) { - if ( $mode == LIST_AND ) { - $list .= " AND "; - } else { - $list .= ","; - } - } else { - $first = false; - } - if ( $mode == LIST_AND || $mode == LIST_SET ) { - $list .= "$field="; - } - if ( !is_numeric( $value ) ) { - $list .= "'" . wfStrencode( $value ) . "'"; - } else { - $list .= $value; - } + $retVal = parent::insertArray( $table, $a, $fname, array() ); + if ( $ignore ) { + $this->setIgnoreErrors( $oldIgnore ); } - return $list; + return $retVal; } function startTimer( $timeout ) { global $IP; wfDebugDieBacktrace( "Database::startTimer() error : mysql_thread_id() not implemented for postgre" ); - $tid = mysql_thread_id( $this->mConn ); - exec( "php $IP/killthread.php $timeout $tid &>/dev/null &" ); + /*$tid = mysql_thread_id( $this->mConn ); + exec( "php $IP/killthread.php $timeout $tid &>/dev/null &" );*/ } - function stopTimer() - { + function tableName( $name ) { + # First run any transformations from the parent object + $name = parent::tableName( $name ); + + # Now quote PG reserved keywords + switch( $name ) { + case 'user': + return '"user"'; + case 'old': + return '"old"'; + default: + return $name; + } } -} + function strencode( $s ) { + return pg_escape_string( $s ); + } -#------------------------------------------------------------------------------ -# Global functions -#------------------------------------------------------------------------------ + # Return the next in a sequence, save the value for retrieval via insertId() + function nextSequenceValue( $seqName ) { + $value = $this->getField(""," nextval('" . $seqName . "')"); + $this->mInsertId = $value; + return $value; + } -/* Standard fail function, called by default when a connection cannot be established - Displays the file cache if possible */ -function wfEmergencyAbort( &$conn ) { - global $wgTitle, $wgUseFileCache, $title, $wgInputEncoding, $wgSiteNotice, $wgOutputEncoding; - - header( "Content-type: text/html; charset=$wgOutputEncoding" ); - $msg = $wgSiteNotice; - if($msg == "") $msg = wfMsgNoDB( "noconnect" ); - $text = $msg; + # USE INDEX clause + # PostgreSQL doesn't have them and returns "" + function useIndexClause( $index ) { + return ''; + } - if($wgUseFileCache) { - if($wgTitle) { - $t =& $wgTitle; - } else { - if($title) { - $t = Title::newFromURL( $title ); - } elseif (@$_REQUEST['search']) { - $search = $_REQUEST['search']; - echo wfMsgNoDB( "searchdisabled" ); - echo wfMsgNoDB( "googlesearch", htmlspecialchars( $search ), $wgInputEncoding ); - wfAbruptExit(); + # REPLACE query wrapper + # PostgreSQL simulates this with a DELETE followed by INSERT + # $row is the row to insert, an associative array + # $uniqueIndexes is an array of indexes. Each element may be either a + # field name or an array of field names + # + # It may be more efficient to leave off unique indexes which are unlikely to collide. + # However if you do this, you run the risk of encountering errors which wouldn't have + # occurred in MySQL + function replace( $table, $uniqueIndexes, $rows, $fname = "Database::replace" ) { + $table = $this->tableName( $table ); + + # Delete rows which collide + if ( $uniqueIndexes ) { + $sql = "DELETE FROM $table WHERE ("; + $first = true; + foreach ( $uniqueIndexes as $index ) { + if ( $first ) { + $first = false; + } else { + $sql .= ") OR ("; + } + if ( is_array( $col ) ) { + $first2 = true; + $sql .= "("; + foreach ( $index as $col ) { + if ( $first2 ) { + $first2 = false; + } else { + $sql .= " AND "; + } + $sql .= "$col = " $this->strencode + + if ( $first ) { + $first = false; + } else { + $sql .= "OR "; + } + $sql .= "$col IN ("; + $indexValues = array(); + foreach ( $rows as $row ) { + $indexValues[] = $row[$col]; + } + $sql .= $this->makeList( $indexValues, LIST_COMMA ) . ") "; + } + $this->query( $sql, $fname ); + } + + # Now insert the rows + $sql = "INSERT INTO $table (" . $this->makeList( array_flip( $rows[0] ) ) .") VALUES "; + $first = true; + foreach ( $rows as $row ) { + if ( $first ) { + $first = false; } else { - $t = Title::newFromText( wfMsgNoDB( "mainpage" ) ); + $sql .= ","; } + $sql .= "(" . $this->makeList( $row, LIST_COMMA ) . ")"; + } + $this->query( $sql, $fname ); + } + + # DELETE where the condition is a join + function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = "Database::deleteJoin" ) { + if ( !$conds ) { + wfDebugDieBacktrace( 'Database::deleteJoin() called with empty $conds' ); } - $cache = new CacheManager( $t ); - if( $cache->isFileCached() ) { - $msg = "

$msg
\n" . - wfMsgNoDB( "cachederror" ) . "

\n"; - - $tag = "
"; - $text = str_replace( - $tag, - $tag . $msg, - $cache->fetchPageText() ); + $delTable = $this->tableName( $delTable ); + $joinTable = $this->tableName( $joinTable ); + $sql = "DELETE FROM $delTable WHERE $delVar IN (SELECT $joinVar FROM $joinTable "; + if ( $conds != '*' ) { + $sql .= "WHERE " . $this->makeList( $conds, LIST_AND ); } + $sql .= ")"; + + $this->query( $sql, $fname ); } - - /* Don't cache error pages! They cause no end of trouble... */ - header( "Cache-control: none" ); - header( "Pragma: nocache" ); - echo $text; - wfAbruptExit(); -} -function wfStrencode( $s ) -{ - return pg_escape_string( $s ); + # Returns the size of a text field, or -1 for "unlimited" + function textFieldSize( $table, $field ) { + $table = $this->tableName( $table ); + $res = $this->query( "SELECT $field FROM $table LIMIT 1", "Database::textFieldLength" ); + $size = pg_field_size( $res, 0 ); + $this->freeResult( $res ); + return $size; + } + + function lowPriorityOption() { + return ''; + } } function wfLimitResult( $limit, $offset ) { diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 447129412f..100a1e279a 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -61,8 +61,16 @@ $wgDBuser = 'wikiuser'; $wgDBtype = "mysql"; # "mysql" for working code and "pgsql" for development/broken code # Database load balancer -$wgDBservers = false; # e.g. array(0 => "larousse", 1 => "pliny") -$wgDBloads = false; # e.g. array(0 => 0.6, 1 => 0.4); +# This is a two-dimensional array, an array of server info structures +# Fields are: +# host: Host name +# dbname: Default database name +# user: DB user +# password: DB password +# type: "mysql" or "pgsql" +# load: ratio of DB_READ load, must be >=0, the sum of all loads must be >0 +# Leave at false to use the single-server variables above +$wgDBservers = false; # Sysop SQL queries $wgAllowSysopQueries = false; # Dangerous if not configured properly. diff --git a/includes/DifferenceEngine.php b/includes/DifferenceEngine.php index 2e3937ee48..30d369e6a7 100644 --- a/includes/DifferenceEngine.php +++ b/includes/DifferenceEngine.php @@ -118,32 +118,33 @@ cellpadding='0' cellspacing='4px' class='diff'> # function loadText() { - global $wgTitle, $wgOut, $wgLang, $wgIsMySQL, $wgIsPg; + global $wgTitle, $wgOut, $wgLang; $fname = "DifferenceEngine::loadText"; - $oldtable=$wgIsPg?'"old"':'old'; + $dbr =& wfGetDB( DB_READ ); if ( 0 == $this->mNewid || 0 == $this->mOldid ) { $wgOut->setArticleFlag( true ); $this->mNewtitle = wfMsg( "currentrev" ); $id = $wgTitle->getArticleID(); - $sql = "SELECT cur_text, cur_user_text, cur_comment FROM cur WHERE cur_id={$id}"; - $res = wfQuery( $sql, DB_READ, $fname ); - if ( 0 == wfNumRows( $res ) ) { return false; } + $s = $dbr->getArray( 'cur', array( 'cur_text', 'cur_user_text', 'cur_comment' ), + array( 'cur_id' => $id ), $fname ); + if ( $s === false ) { + return false; + } - $s = wfFetchObject( $res ); $this->mNewPage = &$wgTitle; $this->mNewtext = $s->cur_text; $this->mNewUser = $s->cur_user_text; $this->mNewComment = $s->cur_comment; } else { - $sql = "SELECT old_namespace,old_title,old_timestamp,old_text,old_flags,old_user_text,old_comment FROM $oldtable WHERE " . - "old_id={$this->mNewid}"; + $s = $dbr->getArray( 'old', array( 'old_namespace','old_title','old_timestamp', 'old_text', + 'old_flags','old_user_text','old_comment' ), array( 'old_id' => $this->mNewid ), $fname ); - $res = wfQuery( $sql, DB_READ, $fname ); - if ( 0 == wfNumRows( $res ) ) { return false; } + if ( $s === false ) { + return false; + } - $s = wfFetchObject( $res ); $this->mNewtext = Article::getRevisionText( $s ); $t = $wgLang->timeanddate( $s->old_timestamp, true ); @@ -153,21 +154,23 @@ cellpadding='0' cellspacing='4px' class='diff'> $this->mNewComment = $s->old_comment; } if ( 0 == $this->mOldid ) { - $use_index=$wgIsMySQL?"USE INDEX (name_title_timestamp)":""; - $sql = "SELECT old_namespace,old_title,old_timestamp,old_text,old_flags,old_user_text,old_comment " . - "FROM $oldtable $use_index WHERE " . - "old_namespace=" . $this->mNewPage->getNamespace() . " AND " . - "old_title='" . wfStrencode( $this->mNewPage->getDBkey() ) . - "' ORDER BY inverse_timestamp LIMIT 1"; - $res = wfQuery( $sql, DB_READ, $fname ); + $s = $dbr->getArray( 'old', + array( 'old_namespace','old_title','old_timestamp','old_text', 'old_flags','old_user_text','old_comment' ), + array( /* WHERE */ + 'old_namespace' => $this->mNewPage->getNamespace(), + 'old_title' => $this->mNewPage->getDBkey() + ), $fname, array( 'ORDER BY' => 'inverse_timestamp', 'USE INDEX' => 'name_title_timestamp' ) + ); } else { - $sql = "SELECT old_namespace,old_title,old_timestamp,old_text,old_flags,old_user_text,old_comment FROM $oldtable WHERE " . - "old_id={$this->mOldid}"; - $res = wfQuery( $sql, DB_READ, $fname ); + $s = $dbr->getArray( 'old', + array( 'old_namespace','old_title','old_timestamp','old_text','old_flags','old_user_text','old_comment'), + array( 'old_id' => $this->mOldid ), + $fname + ); + } + if ( $s === false ) { + return false; } - if ( 0 == wfNumRows( $res ) ) { return false; } - - $s = wfFetchObject( $res ); $this->mOldPage = Title::MakeTitle( $s->old_namespace, $s->old_title ); $this->mOldtext = Article::getRevisionText( $s ); diff --git a/includes/EditPage.php b/includes/EditPage.php index 04ff9c1915..cb2be1bc79 100644 --- a/includes/EditPage.php +++ b/includes/EditPage.php @@ -541,19 +541,19 @@ htmlspecialchars( $wgLang->recodeForEdit( $this->textbox1 ) ) . } /* private */ function mergeChangesInto( &$text ){ - global $wgIsPg; + $fname = 'EditPage::mergeChangesInto'; $oldDate = $this->edittime; - $res = wfQuery("SELECT cur_text FROM cur WHERE cur_id=" . - $this->mTitle->getArticleID() . " FOR UPDATE", DB_WRITE); - $obj = wfFetchObject($res); + $dbw =& wfGetDB( DB_WRITE ); + $obj = $dbr->getArray( 'cur', array( 'cur_text' ), array( 'cur_id' => $this->mTitle->getArticleID() ), + $fname, 'FOR UPDATE' ); $yourtext = $obj->cur_text; $ns = $this->mTitle->getNamespace(); - $title = wfStrencode( $this->mTitle->getDBkey() ); - $oldtable=$wgIsPg?'"old"':'old'; - $res = wfQuery("SELECT old_text,old_flags FROM $oldtable WHERE old_namespace = $ns AND ". - "old_title = '{$title}' AND old_timestamp = '{$oldDate}'", DB_WRITE); - $obj = wfFetchObject($res); + $title = $this->mTitle->getDBkey(); + $obj = $dbw->getArray( 'old', + array( 'old_text','old_flags'), + array( 'old_namespace' => $ns, 'old_title' => $title, 'old_timestamp' => $oldDate ), + $fname ); $oldText = Article::getRevisionText( $obj ); if(wfMerge($oldText, $text, $yourtext, $result)){ diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index a37e1dc20f..a72e84909b 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -392,9 +392,9 @@ function wfDebugDieBacktrace( $msg = '' ) { $f = explode( DIRECTORY_SEPARATOR, $call['file'] ); $file = $f[count($f)-1]; if ( $wgCommandLineMode ) { - $msg .= "$file line {$call['line']}, in "; + $msg .= "$file line {$call['line']} calls "; } else { - $msg .= '
  • ' . $file . " line " . $call['line'] . ', in '; + $msg .= '
  • ' . $file . " line " . $call['line'] . ' calls '; } if( !empty( $call['class'] ) ) $msg .= $call['class'] . '::'; $msg .= $call['function'] . "()"; @@ -420,15 +420,18 @@ function wfNumberOfArticles() /* private */ function wfLoadSiteStats() { global $wgNumberOfArticles, $wgTotalViews, $wgTotalEdits; - if ( -1 != $wgNumberOfArticles ) return; + $fname = 'wfLoadSiteStats'; - $sql = 'SELECT ss_total_views, ss_total_edits, ss_good_articles ' . - 'FROM site_stats WHERE ss_row_id=1'; - $res = wfQuery( $sql, DB_READ, 'wfLoadSiteStats' ); + if ( -1 != $wgNumberOfArticles ) return; + $dbr =& wfGetDB( DB_READ ); + $s = $dbr->getArray( 'site_stats', + array( 'ss_total_views', 'ss_total_edits', 'ss_good_articles' ), + array( 'ss_row_id' => 1 ), $fname + ); - if ( 0 == wfNumRows( $res ) ) { return; } - else { - $s = wfFetchObject( $res ); + if ( $s === false ) { + return; + } else { $wgTotalViews = $s->ss_total_views; $wgTotalEdits = $s->ss_total_edits; $wgNumberOfArticles = $s->ss_good_articles; @@ -505,10 +508,14 @@ function wfRecordUpload( $name, $oldver, $size, $desc, $copyStatus = "", $source global $wgUseCopyrightUpload; $fname = 'wfRecordUpload'; - - $sql = 'SELECT img_name,img_size,img_timestamp,img_description,img_user,' . - "img_user_text FROM image WHERE img_name='" . wfStrencode( $name ) . "'"; - $res = wfQuery( $sql, DB_READ, $fname ); + $dbw =& wfGetDB( DB_WRITE ); + + # img_name must be unique + $indexInfo = $dbw->indexInfo( 'image', 'img_name' ); + if ( $indexInfo && $indexInfo->Non_unique ) { + wfDebugDieBacktrace( 'Database schema not up to date, please run maintenance/archives/patch-img_name_unique.sql' ); + } + $now = wfTimestampNow(); $won = wfInvertTimestamp( $now ); @@ -524,15 +531,24 @@ function wfRecordUpload( $name, $oldver, $size, $desc, $copyStatus = "", $source $now = wfTimestampNow(); $won = wfInvertTimestamp( $now ); - - if ( 0 == wfNumRows( $res ) ) { - $sql = 'INSERT INTO image (img_name,img_size,img_timestamp,' . - "img_description,img_user,img_user_text) VALUES ('" . - wfStrencode( $name ) . "',$size,'{$now}','" . - wfStrencode( $desc ) . "', '" . $wgUser->getID() . - "', '" . wfStrencode( $wgUser->getName() ) . "')"; - wfQuery( $sql, DB_WRITE, $fname ); + # Test to see if the row exists using INSERT IGNORE + # This avoids race conditions by locking the row until the commit, and also + # doesn't deadlock. SELECT FOR UPDATE causes a deadlock for every race condition. + $dbw->insertArray( 'image', + array( + 'img_name' => $name, + 'img_size'=> $size, + 'img_timestamp' => $now, + 'img_description' => $desc, + 'img_user' => $wgUser->getID(), + 'img_user_text' => $wgUser->getName(), + ), $fname, 'IGNORE' + ); + + if ( $dbw->affectedRows() ) { + # Successfully inserted, this is a new image + $sql = 'SELECT cur_id,cur_text FROM cur WHERE cur_namespace=' . Namespace::getImage() . " AND cur_title='" . wfStrencode( $name ) . "'"; @@ -559,6 +575,9 @@ function wfRecordUpload( $name, $oldver, $size, $desc, $copyStatus = "", $source $u->doUpdate(); } } else { + # Collision, this is an update of an image + $s = $dbw->getArray( 'image', array( 'img_name','img_size','img_timestamp','img_description', + 'img_user','img_user_text' ), array( 'img_name' => $name ), $fname, 'FOR UPDATE' ); $s = wfFetchObject( $res ); $sql = 'INSERT INTO oldimage (oi_name,oi_archive_name,oi_size,' . @@ -999,5 +1018,29 @@ function wfInvertTimestamp( $ts ) { ); } +# Reference-counted warning suppression +function wfSuppressWarnings( $end = false ) { + static $suppressCount = 0; + static $originalLevel = false; + + if ( $end ) { + if ( $suppressCount ) { + $suppressCount --; + if ( !$suppressCount ) { + error_reporting( $originalLevel ); + } + } + } else { + if ( !$suppressCount ) { + $originalLevel = error_reporting( E_ALL & ~( E_WARNING | E_NOTICE ) ); + } + $suppressCount++; + } +} + +# Restore error level to previous value +function wfRestoreWarnings() { + wfSuppressWarnings( true ); +} ?> diff --git a/includes/Image.php b/includes/Image.php index 27eeb9d72e..eb9dd8ee7f 100644 --- a/includes/Image.php +++ b/includes/Image.php @@ -253,32 +253,31 @@ class Image //********************************************************************** // Return the image history of this image, line by line. - // start with current version, than old versions. - // use $this->historyLine to check which line to return: + // starts with current version, then old versions. + // uses $this->historyLine to check which line to return: // 0 return line for current version // 1 query for old versions, return first one // 2, ... return next old version from above query function nextHistoryLine() { $fname = "Image::nextHistoryLine()"; - - if ( $this->historyLine == 0 ) // called for the first time, return line from cur - { - $sql = "SELECT img_size,img_description,img_user," . - "img_user_text,img_timestamp, '' AS oi_archive_name FROM image WHERE " . - "img_name='" . wfStrencode( $this->title->getDBkey() ) . "'"; - $this->historyRes = wfQuery( $sql, DB_READ, $fname ); - - if ( 0 == wfNumRows( $this->historyRes ) ) { return FALSE; } - - } else if ( $this->historyLine == 1 ) - { - $sql = "SELECT oi_size AS img_size, oi_description AS img_description," . - "oi_user AS img_user," . - "oi_user_text AS img_user_text, oi_timestamp AS img_timestamp , oi_archive_name FROM oldimage WHERE " . - "oi_name='" . wfStrencode( $this->title->getDBkey() ) . "' " . - "ORDER BY oi_timestamp DESC"; - $this->historyRes = wfQuery( $sql, DB_READ, $fname ); + if ( $this->historyLine == 0 ) {// called for the first time, return line from cur + $dbr =& wfGetDB( DB_READ ); + $this->historyRes = $dbr->select( 'image', + array( 'img_size','img_description','img_user','img_user_text','img_timestamp', "'' AS oi_archive_name" ), + array( 'img_name' => $this->title->getDBkey() ), + $fname + ); + if ( 0 == wfNumRows( $this->historyRes ) ) { + return FALSE; + } + } else if ( $this->historyLine == 1 ) { + $dbr =& wfGetDB( DB_READ ); + $this->historyRes = $dbr->select( 'oldimage', + array( 'oi_size AS img_size', 'oi_description AS img_description', 'oi_user AS img_user', + 'oi_user_text AS img_user_text', 'oi_timestamp AS img_timestamp', 'oi_archive_name' + ), array( 'oi_name' => $this->title->getDBkey() ), $fname, array( 'ORDER BY' => 'oi_timestamp DESC' ) + ); } $this->historyLine ++; diff --git a/includes/ImagePage.php b/includes/ImagePage.php index 6e8d72b6c2..8073cdd5d6 100644 --- a/includes/ImagePage.php +++ b/includes/ImagePage.php @@ -85,18 +85,22 @@ class ImagePage extends Article { $wgOut->addHTML( "

    " . wfMsg( "imagelinks" ) . "

    \n" ); - $sql = "SELECT cur_namespace,cur_title FROM imagelinks,cur WHERE il_to='" . - wfStrencode( $this->mTitle->getDBkey() ) . "' AND il_from=cur_id"; - $res = wfQuery( $sql, DB_READ, "Article::imageLinks" ); + $dbr =& wfGetDB( DB_READ ); + $cur = $dbr->tableName( 'cur' ); + $imagelinks = $dbr->tableName( 'imagelinks' ); + + $sql = "SELECT cur_namespace,cur_title FROM $imagelinks,$cur WHERE il_to=" . + $dbr->addQuotes( $this->mTitle->getDBkey() ) . " AND il_from=cur_id"; + $res = $dbr->query( $sql, DB_READ, "Article::imageLinks" ); - if ( 0 == wfNumRows( $res ) ) { + if ( 0 == $dbr->numRows( $res ) ) { $wgOut->addHtml( "

    " . wfMsg( "nolinkstoimage" ) . "

    \n" ); return; } $wgOut->addHTML( "

    " . wfMsg( "linkstoimage" ) . "

    \n
      " ); $sk = $wgUser->getSkin(); - while ( $s = wfFetchObject( $res ) ) { + while ( $s = $dbr->fetchObject( $res ) ) { $name = Title::MakeTitle( $s->cur_namespace, $s->cur_title ); $link = $sk->makeKnownLinkObj( $name, "" ); $wgOut->addHTML( "
    • {$link}
    • \n" ); @@ -157,6 +161,8 @@ class ImagePage extends Article { $reason = $wgRequest->getVal( 'wpReason' ); $image = $wgRequest->getVal( 'image' ); $oldimage = $wgRequest->getVal( 'oldimage' ); + + $dbw =& wfGetDB( DB_WRITE ); if ( !is_null( $image ) ) { $dest = wfImageDir( $image ); @@ -165,14 +171,9 @@ class ImagePage extends Article { $wgOut->fileDeleteError( "{$dest}/{$image}" ); return; } - $sql = "DELETE FROM image WHERE img_name='" . - wfStrencode( $image ) . "'"; - wfQuery( $sql, DB_WRITE, $fname ); - - $sql = "SELECT oi_archive_name FROM oldimage WHERE oi_name='" . - wfStrencode( $image ) . "'"; - $res = wfQuery( $sql, DB_READ, $fname ); - + $dbw->delete( 'image', array( 'img_name' => $image ) ); + $res = $dbw->select( 'oldimage', array( 'oi_archive_name' ), array( 'oi_name' => $image ) ); + # Squid purging if ( $wgUseSquid ) { $urlArr = Array( @@ -183,7 +184,7 @@ class ImagePage extends Article { $urlArr = Array(); - while ( $s = wfFetchObject( $res ) ) { + while ( $s = $dbr->fetchObject( $res ) ) { $this->doDeleteOldImage( $s->oi_archive_name ); $urlArr[] = $wgInternalServer.wfImageArchiveUrl( $s->oi_archive_name ); } @@ -195,9 +196,7 @@ class ImagePage extends Article { array_push( $wgDeferredUpdateList, $u ); } - $sql = "DELETE FROM oldimage WHERE oi_name='" . - wfStrencode( $image ) . "'"; - wfQuery( $sql, DB_WRITE, $fname ); + $dbw->delete( 'oldimage', array( 'oi_name' => $image ) ); # Image itself is now gone, and database is cleaned. # Now we remove the image description page. @@ -216,10 +215,7 @@ class ImagePage extends Article { wfPurgeSquidServers($urlArr); } $this->doDeleteOldImage( $oldimage ); - $sql = "DELETE FROM oldimage WHERE oi_archive_name='" . - wfStrencode( $oldimage ) . "'"; - wfQuery( $sql, DB_WRITE, $fname ); - + $dbw->delete( 'oldimage', array( 'oi_archive_name' => $oldimage ) ); $deleted = $oldimage; } else { $this->doDeleteArticle( $reason ); # ignore errors @@ -276,8 +272,10 @@ class ImagePage extends Article { return; } $oldver = wfTimestampNow() . "!{$name}"; - $size = wfGetSQL( "oldimage", "oi_size", "oi_archive_name='" . - wfStrencode( $oldimage ) . "'" ); + + $dbr =& wfGetDB( DB_READ ); + $size = $dbr->getField( "oldimage", "oi_size", "oi_archive_name='" . + $dbr->strencode( $oldimage ) . "'" ); if ( ! rename( $curfile, "${archive}/{$oldver}" ) ) { $wgOut->fileRenameError( $curfile, "${archive}/{$oldver}" ); diff --git a/includes/LinkCache.php b/includes/LinkCache.php index cd0c0879e4..8dcba6f55c 100644 --- a/includes/LinkCache.php +++ b/includes/LinkCache.php @@ -131,15 +131,10 @@ class LinkCache { if( $wgLinkCacheMemcached ) $id = $wgMemc->get( $key = $this->getKey( $title ) ); if( ! is_integer( $id ) ) { - $sql = "SELECT cur_id FROM cur WHERE cur_namespace=" . - "{$ns} AND cur_title='" . wfStrencode( $t ) . "'"; - $res = wfQuery( $sql, DB_READ, "LinkCache::addLink" ); - - if ( 0 == wfNumRows( $res ) ) { + $dbr =& wfGetDB( DB_READ ); + $id = $dbr->getField( 'cur', 'cur_id', array( 'cur_namespace' => $ns, 'cur_title' => $t ), $fname ); + if ( !$id ) { $id = 0; - } else { - $s = wfFetchObject( $res ); - $id = $s->cur_id; } if( $wgLinkCacheMemcached ) $wgMemc->add( $key, $id, 3600*24 ); @@ -176,20 +171,21 @@ class LinkCache { } } + $dbr =& wfGetDB( DB_READ ); + $cur = $dbr->tableName( 'cur' ); + $links = $dbr->tableName( 'links' ); + $sql = "SELECT cur_id,cur_namespace,cur_title - FROM cur,links + FROM $cur,$links WHERE cur_id=l_to AND l_from=$id"; - $res = wfQuery( $sql, DB_READ, $fname ); - while( $s = wfFetchObject( $res ) ) { + $res = $dbr->query( $sql, $fname ); + while( $s = $dbr->fetchObject( $res ) ) { $this->addGoodLink( $s->cur_id, Title::makeName( $s->cur_namespace, $s->cur_title ) ); } - $sql = "SELECT bl_to - FROM brokenlinks - WHERE bl_from='{$id}'"; - $res = wfQuery( $sql, DB_READ, "LinkCache::preFill" ); + $res = $dbr->select( 'brokenlinks', array( 'bl_to' ), array( 'bl_from' => $id ), $fname ); while( $s = wfFetchObject( $res ) ) { $this->addBadLink( $s->bl_to ); } @@ -276,18 +272,18 @@ class LinkCache { /* private */ function fillFromLinkscc( $id ){ $id = IntVal( $id ); - $res = wfQuery("SELECT lcc_cacheobj FROM linkscc WHERE lcc_pageid = $id", - DB_READ); - $row = wfFetchObject( $res ); - if( $row == FALSE) - return false; - + $dbr =& wfGetDB( DB_READ ); + $raw = $dbr->getField( 'linkscc', 'lcc_cacheobj', array( 'lcc_pageid' => $id ) ); + if ( $raw === false ) { + return false; + } + $cacheobj = false; if( function_exists( "gzuncompress" ) ) - $cacheobj = @gzuncompress( $row->lcc_cacheobj ); + $cacheobj = @gzuncompress( $raw ); if($cacheobj == FALSE){ - $cacheobj = $row->lcc_cacheobj; + $cacheobj = $raw; } $cc = @unserialize( $cacheobj ); if( isset( $cc->mClassVer ) and ($cc->mClassVer == $this->mClassVer ) ){ @@ -302,51 +298,41 @@ class LinkCache { } /* private */ function saveToLinkscc( $pid ){ - global $wgCompressedPersistentLC, $wgIsMySQL; + global $wgCompressedPersistentLC; if( $wgCompressedPersistentLC and function_exists( "gzcompress" ) ) { - $ser = wfStrencode( gzcompress( serialize( $this ), 3 )); + $ser = gzcompress( serialize( $this ), 3 ); } else { - $ser = wfStrencode( serialize( $this ) ); - } - if ($wgIsMySQL) { - wfQuery("REPLACE INTO linkscc(lcc_pageid,lcc_cacheobj) " . - "VALUES({$pid}, '{$ser}')", DB_WRITE); - } else { - wfQuery("DELETE FROM linkscc WHERE lcc_pageid={$pid}",DB_WRITE); - wfQuery("INSERT INTO linkscc(lcc_pageid,lcc_cacheobj) " . - "VALUES({$pid}, '{$ser}')", DB_WRITE); + $ser = serialize( $this ); } + $db =& wfGetDB( DB_WRITE ); + $db->replace( 'linkscc', array( 'lcc_pageid' ), array( 'lcc_pageid' => $pid, 'lcc_cacheobj' => $ser ) ); } + # Delete linkscc rows which link to here # $pid is a page id /* static */ function linksccClearLinksTo( $pid ){ - global $wgEnablePersistentLC, $wgIsMySQL; + global $wgEnablePersistentLC; if ( $wgEnablePersistentLC ) { + $fname = "LinkCache::linksccClearLinksTo"; $pid = intval( $pid ); - if ($wgIsMySQL) { - wfQuery("DELETE linkscc FROM linkscc,links ". - "WHERE lcc_pageid=links.l_from AND l_to={$pid}", DB_WRITE); - } else { - wfQuery("DELETE FROM linkscc WHERE lcc_pageid IN ". - "(SELECT l_from FROM links WHERE l_to={$pid})", DB_WRITE); - } - wfQuery("DELETE FROM linkscc WHERE lcc_pageid='{$pid}'", DB_WRITE); + $dbw =& wfGetDB( DB_WRITE ); + # Delete linkscc rows which link to here + $dbw->deleteJoin( 'linkscc', 'links', 'lcc_pageid', 'l_from', array( 'l_to' => $pid ), $fname ); + # Delete linkscc row representing this page + $dbw->delete( 'linkscc', array( 'lcc_pageid' => $pid ), $fname); } + } + # Delete linkscc rows with broken links to here # $title is a prefixed db title, for example like Title->getPrefixedDBkey() returns. /* static */ function linksccClearBrokenLinksTo( $title ){ - global $wgEnablePersistentLC,$wgIsMySQL; + global $wgEnablePersistentLC; + $fname = 'LinkCache::linksccClearBrokenLinksTo'; + if ( $wgEnablePersistentLC ) { - $title = wfStrencode( $title ); - if ($wgIsMySQL) { - wfQuery("DELETE linkscc FROM linkscc,brokenlinks ". - "WHERE lcc_pageid=bl_from AND bl_to='{$title}'", DB_WRITE); - } else { - wfQuery("DELETE FROM linkscc WHERE lcc_pageid IN ". - "(SELECT bl_from FROM brokenlinks ". - "WHERE bl_to='{$title}')",DB_WRITE); - } + $dbw =& wfGetDB( DB_WRITE ); + $dbw->deleteJoin( 'linkscc', 'brokenlinks', 'lcc_pageid', 'bl_from', array( 'bl_to' => $title ), $fname ); } } @@ -355,7 +341,8 @@ class LinkCache { global $wgEnablePersistentLC; if ( $wgEnablePersistentLC ) { $pid = intval( $pid ); - wfQuery("DELETE FROM linkscc WHERE lcc_pageid='{$pid}'", DB_WRITE); + $dbw =& wfGetDB( DB_WRITE ); + $dbw->delete( 'linkscc', array( 'lcc_pageid' => $pid ) ); } } } diff --git a/includes/LinksUpdate.php b/includes/LinksUpdate.php index da4f47cf5c..64b9aafced 100644 --- a/includes/LinksUpdate.php +++ b/includes/LinksUpdate.php @@ -9,7 +9,6 @@ class LinksUpdate { { $this->mId = $id; $this->mTitle = $title; - $this->mTitleEnc = wfStrencode( $title ); } @@ -27,10 +26,11 @@ class LinksUpdate { $del = array(); $add = array(); - if( $wgDBtransactions ) { - $sql = "BEGIN"; - wfQuery( $sql, DB_WRITE, $fname ); - } + $dbw =& wfGetDB( DB_WRITE ); + $links = $dbw->tableName( 'links' ); + $brokenlinks = $dbw->tableName( 'brokenlinks' ); + $imagelinks = $dbw->tableName( 'imagelinks' ); + $categorylinks = $dbw->tableName( 'categorylinks' ); #------------------------------------------------------------------------------ # Good links @@ -38,15 +38,14 @@ class LinksUpdate { if ( $wgLinkCache->incrementalSetup( LINKCACHE_GOOD, $del, $add ) ) { # Delete where necessary if ( count( $del ) ) { - $sql = "DELETE FROM links WHERE l_from={$this->mId} AND l_to IN(". + $sql = "DELETE FROM $links WHERE l_from={$this->mId} AND l_to IN(". implode( ",", $del ) . ")"; - wfQuery( $sql, DB_WRITE, $fname ); + $dbw->query( $sql, $fname ); } } else { # Delete everything - $sql = "DELETE FROM links WHERE l_from={$this->mId}"; - wfQuery( $sql, DB_WRITE, $fname ); - + $dbw->delete( 'links', array( 'l_from' => $this->mId ), $fname ); + # Get the addition list $add = $wgLinkCache->getGoodLinks(); } @@ -57,10 +56,9 @@ class LinksUpdate { # 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 - $sql = "INSERT IGNORE INTO links (l_from,l_to) VALUES "; + $sql = "INSERT IGNORE INTO $links (l_from,l_to) VALUES "; $first = true; foreach( $add as $lt => $lid ) { - if ( ! $first ) { $sql .= ","; } $first = false; @@ -68,7 +66,7 @@ class LinksUpdate { } } if ( "" != $sql ) { - wfQuery( $sql, DB_WRITE, $fname ); + $dbw->query( $sql, $fname ); } #------------------------------------------------------------------------------ @@ -77,14 +75,21 @@ class LinksUpdate { if ( $wgLinkCache->incrementalSetup( LINKCACHE_BAD, $del, $add ) ) { # Delete where necessary if ( count( $del ) ) { - $sql = "DELETE FROM brokenlinks WHERE bl_from={$this->mId} AND bl_to IN('" . - implode( "','", array_map( "wfStrencode", $del ) ) . "')"; - wfQuery( $sql, DB_WRITE, $fname ); + $sql = "DELETE FROM $brokenlinks WHERE bl_from={$this->mId} AND bl_to IN("; + $first = true; + foreach( $del as $badTitle ) { + if ( $first ) { + $first = false; + } else { + $sql .= ","; + } + $sql .= $dbw->addQuotes( $badTitle ); + } + $dbw->query( $sql, $fname ); } } else { # Delete all - $sql = "DELETE FROM brokenlinks WHERE bl_from={$this->mId}"; - wfQuery( $sql, DB_WRITE, $fname ); + $dbw->delete( 'brokenlinks', array( 'bl_from' => $this->mId ) ); # Get addition list $add = $wgLinkCache->getBadLinks(); @@ -93,10 +98,10 @@ class LinksUpdate { # Do additions $sql = ""; if ( 0 != count ( $add ) ) { - $sql = "INSERT IGNORE INTO brokenlinks (bl_from,bl_to) VALUES "; + $sql = "INSERT IGNORE INTO $brokenlinks (bl_from,bl_to) VALUES "; $first = true; foreach( $add as $blt ) { - $blt = wfStrencode( $blt ); + $blt = $dbw->strencode( $blt ); if ( ! $first ) { $sql .= ","; } $first = false; @@ -104,13 +109,13 @@ class LinksUpdate { } } if ( "" != $sql ) { - wfQuery( $sql, DB_WRITE, $fname ); + $dbw->query( $sql, $fname ); } #------------------------------------------------------------------------------ # Image links - $sql = "DELETE FROM imagelinks WHERE il_from='{$this->mId}'"; - wfQuery( $sql, DB_WRITE, $fname ); + $sql = "DELETE FROM $imagelinks WHERE il_from='{$this->mId}'"; + $dbw->query( $sql, $fname ); # Get addition list $add = $wgLinkCache->getImageLinks(); @@ -119,7 +124,7 @@ class LinksUpdate { $sql = ""; $image = Namespace::getImage(); if ( 0 != count ( $add ) ) { - $sql = "INSERT IGNORE INTO imagelinks (il_from,il_to) VALUES "; + $sql = "INSERT IGNORE INTO $imagelinks (il_from,il_to) VALUES "; $first = true; foreach( $add as $iname => $val ) { # FIXME: Change all this to avoid unnecessary duplication @@ -127,20 +132,22 @@ class LinksUpdate { if( !$nt ) continue; $nt->invalidateCache(); - $iname = wfStrencode( $iname ); + $iname = $dbw->strencode( $iname ); if ( ! $first ) { $sql .= ","; } $first = false; $sql .= "({$this->mId},'{$iname}')"; } } - if ( "" != $sql ) { wfQuery( $sql, DB_WRITE, $fname ); } + if ( "" != $sql ) { + $dbw->query( $sql, $fname ); + } #------------------------------------------------------------------------------ # Category links if( $wgUseCategoryMagic ) { - $sql = "DELETE FROM categorylinks WHERE cl_from='{$this->mId}'"; - wfQuery( $sql, DB_WRITE, $fname ); + $sql = "DELETE FROM $categorylinks WHERE cl_from='{$this->mId}'"; + $dbw->query( $sql, $fname ); # Get addition list $add = $wgLinkCache->getCategoryLinks(); @@ -148,7 +155,7 @@ class LinksUpdate { # Do the insertion $sql = ""; if ( 0 != count ( $add ) ) { - $sql = "INSERT IGNORE INTO categorylinks (cl_from,cl_to,cl_sortkey) VALUES "; + $sql = "INSERT IGNORE INTO $categorylinks (cl_from,cl_to,cl_sortkey) VALUES "; $first = true; foreach( $add as $cname => $sortkey ) { # FIXME: Change all this to avoid unnecessary duplication @@ -159,19 +166,17 @@ class LinksUpdate { if ( ! $first ) { $sql .= ","; } $first = false; - $sql .= "({$this->mId},'" . wfStrencode( $cname ) . - "','" . wfStrencode( $sortkey ) . "')"; + $sql .= "({$this->mId},'" . $dbw->strencode( $cname ) . + "','" . $dbw->strencode( $sortkey ) . "')"; } } - if ( "" != $sql ) { wfQuery( $sql, DB_WRITE, $fname ); } + if ( "" != $sql ) { + $dbw->query( $sql, $fname ); + } } $this->fixBrokenLinks(); - if( $wgDBtransactions ) { - $sql = "COMMIT"; - wfQuery( $sql, DB_WRITE, $fname ); - } wfProfileOut( $fname ); } @@ -179,23 +184,24 @@ class LinksUpdate { { # Old inefficient update function # Used for rebuilding the link table - global $wgLinkCache, $wgDBtransactions, $wgUseCategoryMagic; $fname = "LinksUpdate::doDumbUpdate"; wfProfileIn( $fname ); - - if( $wgDBtransactions ) { - $sql = "BEGIN"; - wfQuery( $sql, DB_WRITE, $fname ); - } - $sql = "DELETE FROM links WHERE l_from={$this->mId}"; - wfQuery( $sql, DB_WRITE, $fname ); + + $dbw =& wfGetDB( DB_WRITE ); + $links = $dbw->tableName( 'links' ); + $brokenlinks = $dbw->tableName( 'brokenlinks' ); + $imagelinks = $dbw->tableName( 'imagelinks' ); + $categorylinks = $dbw->tableName( 'categorylinks' ); + + $sql = "DELETE FROM $links WHERE l_from={$this->mId}"; + $dbw->query( $sql, $fname ); $a = $wgLinkCache->getGoodLinks(); $sql = ""; if ( 0 != count( $a ) ) { - $sql = "INSERT IGNORE INTO links (l_from,l_to) VALUES "; + $sql = "INSERT IGNORE INTO $links (l_from,l_to) VALUES "; $first = true; foreach( $a as $lt => $lid ) { if ( ! $first ) { $sql .= ","; } @@ -204,47 +210,53 @@ class LinksUpdate { $sql .= "({$this->mId},{$lid})"; } } - if ( "" != $sql ) { wfQuery( $sql, DB_WRITE, $fname ); } + if ( "" != $sql ) { + $dbw->query( $sql, $fname ); + } - $sql = "DELETE FROM brokenlinks WHERE bl_from={$this->mId}"; - wfQuery( $sql, DB_WRITE, $fname ); + $sql = "DELETE FROM $brokenlinks WHERE bl_from={$this->mId}"; + $dbw->query( $sql, $fname ); $a = $wgLinkCache->getBadLinks(); $sql = ""; if ( 0 != count ( $a ) ) { - $sql = "INSERT IGNORE INTO brokenlinks (bl_from,bl_to) VALUES "; + $sql = "INSERT IGNORE INTO $brokenlinks (bl_from,bl_to) VALUES "; $first = true; foreach( $a as $blt ) { - $blt = wfStrencode( $blt ); + $blt = $dbw->strencode( $blt ); if ( ! $first ) { $sql .= ","; } $first = false; $sql .= "({$this->mId},'{$blt}')"; } } - if ( "" != $sql ) { wfQuery( $sql, DB_WRITE, $fname ); } + if ( "" != $sql ) { + $dbw->query( $sql, $fname ); + } - $sql = "DELETE FROM imagelinks WHERE il_from={$this->mId}"; - wfQuery( $sql, DB_WRITE, $fname ); + $sql = "DELETE FROM $imagelinks WHERE il_from={$this->mId}"; + $dbw->query( $sql, $fname ); $a = $wgLinkCache->getImageLinks(); $sql = ""; if ( 0 != count ( $a ) ) { - $sql = "INSERT IGNORE INTO imagelinks (il_from,il_to) VALUES "; + $sql = "INSERT IGNORE INTO $imagelinks (il_from,il_to) VALUES "; $first = true; foreach( $a as $iname => $val ) { - $iname = wfStrencode( $iname ); + $iname = $dbw->strencode( $iname ); if ( ! $first ) { $sql .= ","; } $first = false; $sql .= "({$this->mId},'{$iname}')"; } } - if ( "" != $sql ) { wfQuery( $sql, DB_WRITE, $fname ); } + if ( "" != $sql ) { + $dbw->query( $sql, $fname ); + } if( $wgUseCategoryMagic ) { - $sql = "DELETE FROM categorylinks WHERE cl_from='{$this->mId}'"; - wfQuery( $sql, DB_WRITE, $fname ); + $sql = "DELETE FROM $categorylinks WHERE cl_from='{$this->mId}'"; + $dbw->query( $sql, $fname ); # Get addition list $add = $wgLinkCache->getCategoryLinks(); @@ -252,7 +264,7 @@ class LinksUpdate { # Do the insertion $sql = ""; if ( 0 != count ( $add ) ) { - $sql = "INSERT IGNORE INTO categorylinks (cl_from,cl_to,cl_sortkey) VALUES "; + $sql = "INSERT IGNORE INTO $categorylinks (cl_from,cl_to,cl_sortkey) VALUES "; $first = true; foreach( $add as $cname => $sortkey ) { # FIXME: Change all this to avoid unnecessary duplication @@ -263,18 +275,15 @@ class LinksUpdate { if ( ! $first ) { $sql .= ","; } $first = false; - $sql .= "({$this->mId},'" . wfStrencode( $cname ) . - "','" . wfStrencode( $sortkey ) . "')"; + $sql .= "({$this->mId},'" . $dbw->strencode( $cname ) . + "','" . $dbw->strencode( $sortkey ) . "')"; } } - if ( "" != $sql ) { wfQuery( $sql, DB_WRITE, $fname ); } + if ( "" != $sql ) { + $dbw->query( $sql, $fname ); + } } $this->fixBrokenLinks(); - - if( $wgDBtransactions ) { - $sql = "COMMIT"; - wfQuery( $sql, DB_WRITE, $fname ); - } wfProfileOut( $fname ); } @@ -282,18 +291,22 @@ class LinksUpdate { /* Update any brokenlinks *to* this page */ /* Call for a newly created page, or just to make sure state is consistent */ $fname = "LinksUpdate::fixBrokenLinks"; + + $dbw =& wfGetDB( DB_WRITE ); + $cur = $dbw->tableName( 'cur' ); + $links = $dbw->tableName( 'links' ); - $sql = "SELECT bl_from FROM brokenlinks WHERE bl_to='{$this->mTitleEnc}'"; - $res = wfQuery( $sql, DB_READ, $fname ); - if ( 0 == wfNumRows( $res ) ) { return; } + $res = $dbw->select( 'brokenlinks', array( 'bl_from' ), array( 'bl_to' => $this->mTitle ), + $fname, 'FOR UPDATE' ); + if ( 0 == $dbw->numRows( $res ) ) { return; } # 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 - $sql = "INSERT IGNORE INTO links (l_from,l_to) VALUES "; + $sql = "INSERT IGNORE INTO $links (l_from,l_to) VALUES "; $now = wfTimestampNow(); - $sql2 = "UPDATE cur SET cur_touched='{$now}' WHERE cur_id IN ("; + $sql2 = "UPDATE $cur SET cur_touched='{$now}' WHERE cur_id IN ("; $first = true; - while ( $row = wfFetchObject( $res ) ) { + while ( $row = $dbw->fetchObject( $res ) ) { if ( ! $first ) { $sql .= ","; $sql2 .= ","; } $first = false; @@ -301,13 +314,11 @@ class LinksUpdate { $sql2 .= $row->bl_from; } $sql2 .= ")"; - wfQuery( $sql, DB_WRITE, $fname ); - wfQuery( $sql2, DB_WRITE, $fname ); + $dbw->query( $sql, $fname ); + $dbw->query( $sql2, $fname ); - $sql = "DELETE FROM brokenlinks WHERE bl_to='{$this->mTitleEnc}'"; - wfQuery( $sql, DB_WRITE, $fname ); + $dbw->delete( 'brokenlinks', array( 'bl_to' => $this->mTitle ), $fname ); } - } ?> diff --git a/includes/LoadBalancer.php b/includes/LoadBalancer.php index 94f4f55cff..cfc389475b 100644 --- a/includes/LoadBalancer.php +++ b/includes/LoadBalancer.php @@ -1,6 +1,8 @@ mServers = array(); - $this->mLoads = array(); $this->mConnections = array(); - $this->mUser = false; - $this->mPassword = false; - $this->mDbName = false; $this->mFailFunction = false; $this->mReadIndex = -1; $this->mForce = -1; $this->mLastConn = false; } - function newFromParams( $servers, $loads, $user, $password, $dbName, $failFunction = false ) + function newFromParams( $servers, $failFunction = false ) { $lb = new LoadBalancer; - $lb->initialise( $servers, $loads, $user, $password, $dbName, $failFunction = false ); + $lb->initialise( $servers, $failFunction = false ); return $lb; } - function initialise( $servers, $loads, $user, $password, $dbName, $failFunction = false ) + function initialise( $servers, $failFunction = false ) { $this->mServers = $servers; - $this->mLoads = $loads; - $this->mUser = $user; - $this->mPassword = $password; - $this->mDbName = $dbName; $this->mFailFunction = $failFunction; $this->mReadIndex = -1; $this->mWriteIndex = -1; $this->mForce = -1; $this->mConnections = array(); $this->mLastConn = false; + $this->mLoads = array(); + + foreach( $servers as $i => $server ) { + $this->mLoads[$i] = $server['load']; + } + wfSeedRandom(); } @@ -99,7 +99,7 @@ class LoadBalancer { do { $i = $this->pickRandom( $loads ); if ( $i !== false ) { - wfDebug( "Using reader #$i: {$this->mServers[$i]}\n" ); + wfDebug( "Using reader #$i: {$this->mServers[$i]['host']}\n" ); $conn =& $this->getConnection( $i ); if ( !$conn->isOpen() ) { @@ -148,11 +148,10 @@ class LoadBalancer { } else { # Explicit index if ( !array_key_exists( $i, $this->mConnections) || !$this->mConnections[$i]->isOpen() ) { - $this->mConnections[$i] = Database::newFromParams( $this->mServers[$i], $this->mUser, - $this->mPassword, $this->mDbName, 1 ); + $this->mConnections[$i] = $this->makeConnection( $this->mServers[$i] ); } if ( !$this->mConnections[$i]->isOpen() ) { - wfDebug( "Failed to connect to database $i at {$this->mServers[$i]}\n" ); + wfDebug( "Failed to connect to database $i at {$this->mServers[$i]['host']}\n" ); if ( $fail ) { $this->reportConnectionError( $this->mConnections[$i] ); } @@ -163,6 +162,18 @@ class LoadBalancer { return $this->mLastConn; } + /* private */ function makeConnection( &$server ) { + extract( $server ); + # Get class for this database type + $class = 'Database' . ucfirst( $type ); + if ( !class_exists( $class ) ) { + require_once( "$class.php" ); + } + + # Create object + return new $class( $host, $user, $password, $dbname, 1 ); + } + function reportConnectionError( &$conn ) { if ( !is_object( $conn ) ) { diff --git a/includes/LogPage.php b/includes/LogPage.php index 6be15fed9b..31f598c7a5 100644 --- a/includes/LogPage.php +++ b/includes/LogPage.php @@ -18,13 +18,16 @@ class LogPage { function getContent( $defaulttext = "
        \n
      " ) { - $sql = "SELECT cur_id,cur_text,cur_timestamp FROM cur " . - "WHERE cur_namespace=" . Namespace::getWikipedia() . " AND " . - "cur_title='" . wfStrencode($this->mTitle ) . "'"; - $res = wfQuery( $sql, DB_READ, "LogPage::getContent" ); + $fname = 'LogPage::getContent'; - if( wfNumRows( $res ) > 0 ) { - $s = wfFetchObject( $res ); + $dbw =& wfGetDB( DB_WRITE ); + $s = $dbw->getArray( 'cur', + array( 'cur_id','cur_text','cur_timestamp' ), + array( 'cur_namespace' => Namespace::getWikipedia(), 'cur_title' => $this->mTitle ), + $fname, 'FOR UPDATE' + ); + + if( $s !== false ) { $this->mId = $s->cur_id; $this->mContent = $s->cur_text; $this->mTimestamp = $s->cur_timestamp; @@ -52,31 +55,49 @@ class LogPage { global $wgUser; $fname = "LogPage::saveContent"; + + $dbw =& wfGetDB( DB_WRITE ); $uid = $wgUser->getID(); - $ut = wfStrencode( $wgUser->getName() ); if( !$this->mContentLoaded ) return false; $this->mTimestamp = $now = wfTimestampNow(); $won = wfInvertTimestamp( $now ); if($this->mId == 0) { - $sql = "INSERT INTO cur (cur_timestamp,cur_user,cur_user_text, - cur_namespace,cur_title,cur_text,cur_comment,cur_restrictions, - inverse_timestamp,cur_touched) - VALUES ('{$now}', {$uid}, '{$ut}', " . - Namespace::getWikipedia() . ", '" . - wfStrencode( $this->mTitle ) . "', '" . - wfStrencode( $this->mContent ) . "', '" . - wfStrencode( $this->mComment ) . "', 'sysop', '{$won}','{$now}')"; - wfQuery( $sql, DB_WRITE, $fname ); - $this->mId = wfInsertId(); + $seqVal = $dbw->nextSequenceValue( 'cur_cur_id_seq' ); + + # Note: this query will deadlock if another thread has called getContent(), + # at least in MySQL 4.0.17 InnoDB + $dbw->insertArray( 'cur', + array( + 'cur_id' => $seqVal, + 'cur_timestamp' => $now, + 'cur_user' => $uid, + 'cur_user_text' => $wgUser->getName(), + 'cur_namespace' => NS_WIKIPEDIA, + 'cur_title' => $this->mTitle, + 'cur_text' => $this->mContent, + 'cur_comment' => $this->mComment, + 'cur_restrictions' => 'sysop', + 'inverse_timestamp' => $won, + 'cur_touched' => $now, + ), $fname + ); + $this->mId = $dbw->insertId(); } else { - $sql = "UPDATE cur SET cur_timestamp='{$now}', " . - "cur_user={$uid}, cur_user_text='{$ut}', " . - "cur_text='" . wfStrencode( $this->mContent ) . "', " . - "cur_comment='" . wfStrencode( $this->mComment ) . "', " . - "cur_restrictions='sysop', inverse_timestamp='{$won}', cur_touched='{$now}' " . - "WHERE cur_id={$this->mId}"; - wfQuery( $sql, DB_WRITE, $fname ); + $dbw->updateArray( 'cur', + array( /* SET */ + 'cur_timestamp' => $now, + 'cur_user' => $uid, + 'cur_user_text' => $wgUser->getName(), + 'cur_text' => $this->mContent, + 'cur_comment' => $this->mComment, + 'cur_restrictions' => 'sysop', + 'inverse_timestamp' => $won, + 'cur_touched' => $now, + ), array( /* WHERE */ + 'cur_id' => $this->mId + ), $fname + ); } # And update recentchanges diff --git a/includes/Math.php b/includes/Math.php index 8b03af6adc..827c1167df 100644 --- a/includes/Math.php +++ b/includes/Math.php @@ -54,10 +54,10 @@ class MathRenderer { return $this->_error( "math_notexvc" ); } $cmd = $wgTexvc." ". - escapeshellarg($wgTmpDirectory)." ". - escapeshellarg($wgMathDirectory)." ". - escapeshellarg($this->tex)." ". - escapeshellarg($wgInputEncoding); + wfEscapeShellArg($wgTmpDirectory)." ". + wfEscapeShellArg($wgMathDirectory)." ". + wfEscapeShellArg($this->tex)." ". + wfEscapeShellArg($wgInputEncoding); wfDebug( "TeX: $cmd" ); $contents = `$cmd`; @@ -79,9 +79,6 @@ class MathRenderer { $this->html = substr($outdata, 0, $i); $this->mathml = substr($outdata, $i+1); - - $sql_html = "'".wfStrencode($this->html)."'"; - $sql_mathml = "'".wfStrencode($this->mathml)."'"; } else if (($retval == "c") || ($retval == "m") || ($retval == "l")) { $this->html = substr ($contents, 33); if ($retval == "c") @@ -90,20 +87,14 @@ class MathRenderer { $this->conservativeness = 1; else $this->conservativeness = 0; - $sql_html = "'".wfStrencode($this->html)."'"; - $this->mathml = ''; - $sql_mathml = 'NULL'; + $this->mathml = NULL; } else if ($retval == "X") { - $outhtml = ''; + $outhtml = NULL; $this->mathml = substr ($contents, 33); - $sql_html = 'NULL'; - $sql_mathml = "'".wfStrencode($this->mathml)."'"; $this->conservativeness = 0; } else if ($retval == "+") { - $this->outhtml = ''; - $this->mathml = ''; - $sql_html = 'NULL'; - $sql_mathml = 'NULL'; + $this->outhtml = NULL; + $this->mathml = NULL; $this->conservativeness = 0; } else { $errbit = htmlspecialchars( substr($contents, 1) ); @@ -125,13 +116,21 @@ class MathRenderer { } # Now save it back to the DB: - $outmd5_sql = wfStrencode(pack("H32", $this->hash)); + $outmd5_sql = pack("H32", $this->hash); - $md5_sql = wfStrencode( pack("H32", $this->md5) ); # Binary packed, not hex - $sql = "REPLACE INTO math VALUES ('".$md5_sql."', '".$outmd5_sql."', ".$this->conservativeness.", ".$sql_html.", ".$sql_mathml.")"; + $md5_sql = pack("H32", $this->md5); # Binary packed, not hex + + $dbw =& wfGetDB( DB_WRITE ); + $dbw->replace( 'math', array( 'math_inputhash' ), + array( + 'math_inputhash' => $md5_sql, + 'math_outputhash' => $outmd5_sql, + 'math_html_conservativeness' => $this->conservativeness, + 'math_html' => $outhtml, + 'math_mathml' => $mathml, + ), $fname, array( 'IGNORE' ) + ); - $res = wfQuery( $sql, DB_WRITE, "MathRenderer::render" ); - # we don't really care if it fails } return $this->_doRender(); @@ -147,14 +146,17 @@ class MathRenderer { function _recall() { global $wgMathDirectory; - + $fname = 'MathRenderer::_recall'; + $this->md5 = md5( $this->tex ); - - $md5_sql = wfStrencode( pack("H32", $this->md5) ); # Binary packed, not hex - $sql = "SELECT math_outputhash,math_html_conservativeness,math_html,math_mathml FROM math WHERE math_inputhash = '$md5_sql'"; - $res = wfQuery( $sql, DB_READ, "MathRenderer::_recall" ); - - if( $rpage = wfFetchObject ( $res ) ) { + $dbr =& wfGetDB( DB_READ ); + $rpage = $dbr->getArray( 'math', + array( 'math_outputhash','math_html_conservativeness','math_html','math_mathml' ), + array( 'math_inputhash' => pack("H32", $this->md5), # Binary packed, not hex + $fname + ); + + if( $rpage !== false ) { # Tailing 0x20s can get dropped by the database, add it back on if necessary: $xhash = unpack( "H32md5", $rpage->math_outputhash . " " ); $this->hash = $xhash ['md5']; diff --git a/includes/MessageCache.php b/includes/MessageCache.php index 9462a520ac..a6f00f5f91 100755 --- a/includes/MessageCache.php +++ b/includes/MessageCache.php @@ -37,6 +37,7 @@ class MessageCache global $wgAllMessagesEn; if ( $this->mDisable ) { + wfDebug( "MessageCache::load(): disabled\n" ); return true; } @@ -47,6 +48,7 @@ class MessageCache # If there's nothing in memcached, load all the messages from the database if ( !$this->mCache ) { + wfDebug( "MessageCache::load(): loading all messages\n" ); $this->lock(); # Other threads don't need to load the messages if another thread is doing it. $this->mMemc->set( $this->mMemcKey, "loading", MSG_LOAD_TIMEOUT ); @@ -64,6 +66,7 @@ class MessageCache } if ( !is_array( $this->mCache ) ) { + wfMsg( "MessageCache::load(): individual message mode\n" ); # If it is 'loading' or 'error', switch to individual message mode, otherwise disable # Causing too much DB load, disabling -- TS $this->mDisable = true; @@ -86,18 +89,20 @@ class MessageCache # Loads all cacheable messages from the database function loadFromDB() { - global $wgLoadBalancer; $fname = "MessageCache::loadFromDB"; - $wgLoadBalancer->force(-1); - $sql = "SELECT cur_title,cur_text FROM cur WHERE cur_is_redirect=0 AND cur_namespace=" . NS_MEDIAWIKI; - $res = wfQuery( $sql, DB_READ, $fname ); - $wgLoadBalancer->force(0); + $dbr =& wfGetDB( DB_READ ); + $res = $dbr->select( 'cur', + array( 'cur_title', 'cur_text' ), + array( 'cur_is_redirect' => 0, 'cur_namespace' => NS_MEDIAWIKI ), + $fname + ); + $this->mCache = array(); - for ( $row = wfFetchObject( $res ); $row; $row = wfFetchObject( $res ) ) { + for ( $row = $dbr->fetchObject( $res ); $row; $row = $dbr->fetchObject( $res ) ) { $this->mCache[$row->cur_title] = $row->cur_text; } - wfFreeResult( $res ); + $dbr->freeResult( $res ); } # Not really needed anymore @@ -176,7 +181,8 @@ class MessageCache # If it wasn't in the cache, load each message from the DB individually if ( !$message && $useDB) { - $result = wfGetArray( "cur", array("cur_text"), + $dbr =& wfGetDB( DB_READ ); + $result = $dbr->getArray( "cur", array("cur_text"), array( "cur_namespace" => NS_MEDIAWIKI, "cur_title" => $title ), "MessageCache::get" ); if ( $result ) { diff --git a/includes/ObjectCache.php b/includes/ObjectCache.php index ce511ff37b..5bcfde777f 100644 --- a/includes/ObjectCache.php +++ b/includes/ObjectCache.php @@ -285,16 +285,20 @@ class /* abstract */ SqlBagOStuff extends BagOStuff { class MediaWikiBagOStuff extends SqlBagOStuff { function _doquery($sql) { - return wfQuery($sql, DB_READ, "MediaWikiBagOStuff:_doquery"); + $dbw = wfGetDB( DB_WRITE ); + return $dbw->query($sql, "MediaWikiBagOStuff:_doquery"); } function _fetchobject($result) { - return wfFetchObject($result); + $dbw = wfGetDB( DB_WRITE ); + return $dbw->fetchObject($result); } function _freeresult($result) { - return wfFreeResult($result); + $dbw = wfGetDB( DB_WRITE ); + return $dbw->freeResult($result); } function _dberror($result) { - return wfLastError(); + $dbw = wfGetDB( DB_WRITE ); + return $dbw->lastError(); } function _maxdatetime() { return "9999-12-31 12:59:59"; @@ -303,7 +307,8 @@ class MediaWikiBagOStuff extends SqlBagOStuff { return gmdate( "Y-m-d H:i:s", $ts ); } function _strencode($s) { - return wfStrEncode($s); + $dbw = wfGetDB( DB_WRITE ); + return $dbw->strencode($s); } } diff --git a/includes/OutputPage.php b/includes/OutputPage.php index ac75966b70..5e17bbf664 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -307,7 +307,7 @@ class OutputPage { { global $wgUser, $wgLang, $wgDebugComments, $wgCookieExpiration; global $wgInputEncoding, $wgOutputEncoding, $wgLanguageCode; - global $wgDebugRedirects, $wgMimeType, $wgProfiling; + global $wgDebugRedirects, $wgMimeType, $wgProfiler; if( $this->mDoNothing ){ return; } diff --git a/includes/PageHistory.php b/includes/PageHistory.php index 3eeaa9d419..adc4790728 100644 --- a/includes/PageHistory.php +++ b/includes/PageHistory.php @@ -17,7 +17,7 @@ class PageHistory { function history() { - global $wgUser, $wgOut, $wgLang, $wgIsMySQL, $wgIsPg; + global $wgUser, $wgOut, $wgLang; # If page hasn't changed, client can cache this @@ -54,17 +54,20 @@ class PageHistory { $namespace = $this->mTitle->getNamespace(); $title = $this->mTitle->getText(); - $use_index=$wgIsMySQL?"USE INDEX (name_title_timestamp)":""; - $oldtable=$wgIsPg?'"old"':'old'; + + $db =& wfGetDB( DB_READ ); + $use_index = $db->useIndexClause( 'name_title_timestamp' ); + $oldtable = $db->tableName( 'old' ); + $sql = "SELECT old_id,old_user," . "old_comment,old_user_text,old_timestamp,old_minor_edit ". "FROM $oldtable $use_index " . "WHERE old_namespace={$namespace} AND " . - "old_title='" . wfStrencode( $this->mTitle->getDBkey() ) . "' " . + "old_title='" . $db->strencode( $this->mTitle->getDBkey() ) . "' " . "ORDER BY inverse_timestamp".wfLimitResult($limitplus,$rawoffset); - $res = wfQuery( $sql, DB_READ, $fname ); + $res = $db->query( $sql, $fname ); - $revs = wfNumRows( $res ); + $revs = $db->numRows( $res ); if( $revs < $limitplus ) // the sql above tries to fetch one extra $this->linesonpage = $revs; @@ -98,7 +101,7 @@ class PageHistory { $counter++ ); } - while ( $line = wfFetchObject( $res ) ) { + while ( $line = $db->fetchObject( $res ) ) { $s .= $this->historyLine( $line->old_timestamp, $line->old_user, $line->old_user_text, $namespace, diff --git a/includes/Parser.php b/includes/Parser.php index 8a24bf178a..53a5bcdecc 100644 --- a/includes/Parser.php +++ b/includes/Parser.php @@ -343,6 +343,8 @@ class Parser # This method generates the list of subcategories and pages for a category function oldCategoryMagic () { global $wgLang , $wgUser ; + $fname = 'Parser::oldCategoryMagic'; + if ( !$this->mOptions->getUseCategoryMagic() ) return ; # Doesn't use categories at all $cns = Namespace::getCategory() ; @@ -359,10 +361,15 @@ class Parser $id = $this->mTitle->getArticleID() ; # FIXME: add limits - $t = wfStrencode( $this->mTitle->getDBKey() ); - $sql = "SELECT DISTINCT cur_title,cur_namespace FROM cur,categorylinks WHERE cl_to='$t' AND cl_from=cur_id ORDER BY cl_sortkey" ; - $res = wfQuery ( $sql, DB_READ ) ; - while ( $x = wfFetchObject ( $res ) ) $data[] = $x ; + $dbr =& wfGetDB( DB_READ ); + $cur = $dbr->tableName( 'cur' ); + $categorylinks = $dbr->tableName( 'categorylinks' ); + + $t = $dbr->strencode( $this->mTitle->getDBKey() ); + $sql = "SELECT DISTINCT cur_title,cur_namespace FROM $cur,$categorylinks " . + "WHERE cl_to='$t' AND cl_from=cur_id ORDER BY cl_sortkey" ; + $res = $dbr->query( $sql, $fname ) ; + while ( $x = $dbr->fetchObject ( $res ) ) $data[] = $x ; # For all pages that link to this category foreach ( $data AS $x ) @@ -377,7 +384,7 @@ class Parser array_push ( $articles , $sk->makeLink ( $t ) ) ; # Page in this category } } - wfFreeResult ( $res ) ; + $dbr->freeResult ( $res ) ; # Showing subcategories if ( count ( $children ) > 0 ) { @@ -393,7 +400,6 @@ class Parser $r .= implode ( ', ' , $articles ) ; } - return $r ; } @@ -419,12 +425,15 @@ class Parser $id = $this->mTitle->getArticleID() ; # FIXME: add limits - $t = wfStrencode( $this->mTitle->getDBKey() ); - $sql = "SELECT DISTINCT cur_title,cur_namespace,cl_sortkey FROM -cur,categorylinks WHERE cl_to='$t' AND cl_from=cur_id ORDER BY -cl_sortkey" ; - $res = wfQuery ( $sql, DB_READ ) ; - while ( $x = wfFetchObject ( $res ) ) + $dbr =& wfGetDB( DB_READ ); + $cur = $dbr->tableName( 'cur' ); + $categorylinks = $dbr->tableName( 'categorylinks' ); + + $t = $dbr->strencode( $this->mTitle->getDBKey() ); + $sql = "SELECT DISTINCT cur_title,cur_namespace,cl_sortkey FROM " . + "$cur,$categorylinks WHERE cl_to='$t' AND cl_from=cur_id ORDER BY cl_sortkey" ; + $res = $dbr->query ( $sql ) ; + while ( $x = $dbr->fetchObject ( $res ) ) { $t = $ns = $wgLang->getNsText ( $x->cur_namespace ) ; if ( $t != '' ) $t .= ':' ; @@ -448,7 +457,7 @@ cl_sortkey" ; array_push ( $articles_start_char, $wgLang->firstChar( $x->cl_sortkey ) ) ; } } - wfFreeResult ( $res ) ; + $dbr->freeResult ( $res ) ; $ti = $this->mTitle->getText() ; @@ -665,6 +674,9 @@ cl_sortkey" ; # parse the wiki syntax used to render tables function doTableStuff ( $t ) { + $fname = 'Parser::doTableStuff'; + wfProfileIn( $fname ); + $t = explode ( "\n" , $t ) ; $td = array () ; # Is currently a td tag open? $ltd = array () ; # Was it TD or TH? @@ -760,6 +772,7 @@ cl_sortkey" ; $t = implode ( "\n" , $t ) ; # $t = $this->removeHTMLtags( $t ); + wfProfileOut( $fname ); return $t ; } @@ -2025,9 +2038,14 @@ cl_sortkey" ; # Return an HTML link for the "ISBN 123456" text /* private */ function magicISBN( $text ) { global $wgLang; + $fname = 'Parser::magicISBN'; + wfProfileIn( $fname ); $a = split( 'ISBN ', " $text" ); - if ( count ( $a ) < 2 ) return $text; + if ( count ( $a ) < 2 ) { + wfProfileOut( $fname ); + return $text; + } $text = substr( array_shift( $a ), 1); $valid = '0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ'; @@ -2054,6 +2072,7 @@ cl_sortkey" ; $text .= $x; } } + wfProfileOut( $fname ); return $text; } diff --git a/includes/Profiling.php b/includes/Profiling.php index ed58bf08d8..e27c766e09 100755 --- a/includes/Profiling.php +++ b/includes/Profiling.php @@ -40,7 +40,7 @@ class Profiler { global $wgDebugFunctionEntry; if ( $wgDebugFunctionEntry && function_exists( "wfDebug" ) ) { - wfDebug( "Entering $functionname\n" ); + wfDebug( str_repeat( " ", count( $this->mWorkStack ) ) . "Entering $functionname\n" ); } array_push( $this->mWorkStack, array($functionname, count( $this->mWorkStack ), microtime() ) ); } @@ -50,7 +50,7 @@ class Profiler global $wgDebugProfiling, $wgDebugFunctionEntry; if ( $wgDebugFunctionEntry && function_exists( "wfDebug" ) ) { - wfDebug( "Exiting $functionname\n" ); + wfDebug( str_repeat( " ", count( $this->mWorkStack ) - 1 ) . "Exiting $functionname\n" ); } $bit = array_pop( $this->mWorkStack ); @@ -77,8 +77,11 @@ class Profiler } } - function getOutput( $scriptStart, $scriptElapsed ) + function getOutput() { + global $wgDebugFunctionEntry; + $wgDebugFunctionEntry = false; + if( !count( $this->mStack ) ) { return "No profiling output\n"; } @@ -148,19 +151,21 @@ class Profiler /* static */ function logToDB($name, $timeSum, $eventCount) { - $name = wfStrencode( $name ); - $sql = "UPDATE profiling ". + $dbw =& wfGetDB( DB_WRITE ); + $profiling = $dbw->tableName( 'profiling' ); + + $name = $dbw->strencode( $name ); + $sql = "UPDATE $profiling ". "SET pf_count=pf_count+{$eventCount}, ". "pf_time=pf_time + {$timeSum} ". "WHERE pf_name='{$name}'"; - wfQuery($sql , DB_WRITE); + $dbw->query($sql); - $rc = wfAffectedRows(); + $rc = $dbw->affectedRows(); if( $rc == 0) { - $sql = "INSERT IGNORE INTO profiling (pf_name,pf_count,pf_time) ". + $sql = "INSERT IGNORE INTO $profiling (pf_name,pf_count,pf_time) ". "VALUES ('{$name}', {$eventCount}, {$timeSum}) "; - wfQuery($sql , DB_WRITE); - $rc = wfAffectedRows(); + $dbw->query($sql , DB_WRITE); } // When we upgrade to mysql 4.1, the insert+update // can be merged into just a insert with this construct added: diff --git a/includes/QueryPage.php b/includes/QueryPage.php index 43804cee26..77a55322ca 100644 --- a/includes/QueryPage.php +++ b/includes/QueryPage.php @@ -65,38 +65,67 @@ class QueryPage { $sname = $this->getName(); $fname = get_class($this) . "::doQuery"; - $sql = $this->getSQL( $offset, $limit ); + $sql = $this->getSQL(); + $dbr =& wfGetDB( DB_READ ); + $dbw =& wfGetDB( DB_WRITE ); + $querycache = $dbr->tableName( 'querycache' ); $wgOut->setSyndicated( true ); + $res = false; if ( $this->isExpensive() ) { - $type = wfStrencode( $sname ); $recache = $wgRequest->getBool( "recache" ); if( $recache ) { # Clear out any old cached data - $res = wfQuery( "DELETE FROM querycache WHERE qc_type='$type'", DB_WRITE, $fname ); - - # Save results into the querycache table + $dbw->delete( 'querycache', array( 'qc_type' => $sname ), $fname ); + + # Do query on the (possibly out of date) slave server $maxstored = 1000; - $res = wfQuery( - "INSERT INTO querycache(qc_type,qc_namespace,qc_title,qc_value) " . - $this->getSQL() . - $this->getOrderLimit( 0, $maxstored ), - DB_WRITE, $fname ); + $res = $dbr->query( $sql . $this->getOrderLimit( 0, $maxstored ), $fname ); + + # Fetch results + $insertSql = "INSERT INTO $querycache (qc_type,qc_namespace,qc_title,qc_value) VALUES "; + $first = true; + while ( $row = $dbr->fetchObject( $res ) ) { + if ( $first ) { + $first = false; + } else { + $insertSql .= ","; + } + $insertSql .= "(" . + $dbw->addQuotes( $row->type ) . "," . + $dbw->addQuotes( $row->namespace ) . "," . + $dbw->addQuotes( $row->title ) . "," . + $dbw->addQuotes( $row->value ) . ")" + } + + # Save results into the querycache table on the master + $dbw->query( $insertSql, $fname ); + + # Set result pointer to allow reading for display + $numRows = $dbr->numRows( $res ); + if ( $numRows <= $offset ) { + $num = 0; + } else { + $dbr->dataSeek( $res, $offset ); + $num = max( $limit, $numRows - $offset ); + } } if( $wgMiserMode || $recache ) { + $type = $dbr->strencdode( $sname ); $sql = "SELECT qc_type as type, qc_namespace as namespace,qc_title as title, qc_value as value - FROM querycache WHERE qc_type='$type'"; + FROM $querycache WHERE qc_type='$type'"; } if( $wgMiserMode ) { $wgOut->addWikiText( wfMsg( "perfcached" ) ); } } - - $res = wfQuery( $sql . $this->getOrderLimit( $offset, $limit ), DB_READ, $fname ); + if ( $res === false ) { + $res = $dbr->query( $sql . $this->getOrderLimit( $offset, $limit ), $fname ); + $num = $dbr->numRows($res); + } - $num = wfNumRows($res); $sk = $wgUser->getSkin( ); @@ -110,11 +139,12 @@ class QueryPage { $wgOut->addHTML( "
      {$sl}

      \n" ); $s = "
        "; - while ( $obj = wfFetchObject( $res ) ) { + # Only read at most $num rows, because $res may contain the whole 1000 + for ( $i = 0; $i < $num && $obj = $dbr->fetchObject( $res ); $i++ ) { $format = $this->formatResult( $sk, $obj ); $s .= "
      1. {$format}
      2. \n"; } - wfFreeResult( $res ); + $dbr->freeResult( $res ); $s .= "
      "; $wgOut->addHTML( $s ); $wgOut->addHTML( "

      {$sl}

      \n" ); @@ -131,13 +161,14 @@ class QueryPage { $this->feedUrl() ); $feed->outHeader(); - $sql = $this->getSQL( 0, 50 ); - $res = wfQuery( $sql, DB_READ, "QueryPage::doFeed" ); - while( $obj = wfFetchObject( $res ) ) { + $dbr = wfGetDB( DB_READ ); + $sql = $this->getSQL() . $this->getOrderLimit( 0, 50 ); + $res = $dbr->query( $sql, "QueryPage::doFeed" ); + while( $obj = $dbr->fetchObject( $res ) ) { $item = $this->feedResult( $obj ); if( $item ) $feed->outItem( $item ); } - wfFreeResult( $res ); + $dbr->freeResult( $res ); $feed->outFooter(); return true; diff --git a/includes/RawPage.php b/includes/RawPage.php index 003ab452f7..9179a5e6b1 100644 --- a/includes/RawPage.php +++ b/includes/RawPage.php @@ -56,13 +56,16 @@ class RawPage { } else { echo $this->getrawtext(); } - wfAbruptExit(); + $wgOut->disable(); } function getrawtext () { - global $wgInputEncoding, $wgLang, $wgIsPg; + global $wgInputEncoding, $wgLang; + $fname = 'RawPage::getrawtext'; + if( !$this->mTitle ) return ''; - $t = wfStrencode( $this->mTitle->getDBKey() ); + $dbr = wfGetDB( DB_READ ); + $t = $dbr->strencode( $this->mTitle->getDBKey() ); $ns = $this->mTitle->getNamespace(); # special case if($ns == NS_MEDIAWIKI) { @@ -73,7 +76,7 @@ class RawPage { } # else get it from the DB if(!empty($this->mOldId)) { - $oldtable=$wgIsPg?'"old"':'old'; + $oldtable = $dbr->tableName( 'old', DB_READ ); $sql = "SELECT old_text AS text,old_timestamp AS timestamp,". "old_user AS user,old_flags AS flags FROM $oldtable " . "WHERE old_id={$this->mOldId}"; @@ -82,8 +85,8 @@ class RawPage { "cur_restrictions as restrictions,cur_comment as comment,cur_text as text FROM cur " . "WHERE cur_namespace=$ns AND cur_title='$t'"; } - $res = wfQuery( $sql, DB_READ ); - if( $s = wfFetchObject( $res ) ) { + $res = $dbr->query( $sql, $fname ); + if( $s = $dbr->fetchObject( $res ) ) { $rawtext = Article::getRevisionText( $s, "" ); if($wgInputEncoding != $this->mCharset) $rawtext = $wgLang->iconv( $wgInputEncoding, $this->mCharset, $rawtext ); diff --git a/includes/SearchEngine.php b/includes/SearchEngine.php index ff17c89958..bb2ed08b6e 100644 --- a/includes/SearchEngine.php +++ b/includes/SearchEngine.php @@ -157,21 +157,17 @@ class SearchEngine { { global $wgUser, $wgTitle, $wgOut, $wgLang, $wgRequest; global $wgDisableTextSearch, $wgInputEncoding; - global $wgLoadBalancer; - - $wgLoadBalancer->force(-1); - - $fname = "SearchEngine::showResults"; + $fname = "SearchEngine::showResults"; $search = $wgRequest->getText( 'search' ); $powersearch = $this->powersearch(); /* Need side-effects here? */ $this->setupPage(); - + $sk = $wgUser->getSkin(); $header = wfMsg( "searchresulttext", $sk->makeKnownLink( - wfMsg( "searchhelppage" ), wfMsg( "searchingwikipedia" ) ) ); + wfMsg( "searchhelppage" ), wfMsg( "searchingwikipedia" ) ) ); $wgOut->addHTML( $header ); $this->parseQuery(); @@ -284,8 +280,7 @@ class SearchEngine { $wgOut->addHTML( "

      {$sl}

      \n" ); $wgOut->addHTML( $powersearch ); } - $wgLoadBalancer->force(0); - } + } function legalSearchChars() { diff --git a/includes/SearchUpdate.php b/includes/SearchUpdate.php index c7b9c6103c..55bad7907d 100644 --- a/includes/SearchUpdate.php +++ b/includes/SearchUpdate.php @@ -25,19 +25,21 @@ class SearchUpdate { function doUpdate() { - global $wgDBminWordLen, $wgLang, $wgDisableSearchUpdate, $wgIsMySQL; + global $wgDBminWordLen, $wgLang, $wgDisableSearchUpdate; if( $wgDisableSearchUpdate || !$this->mId ) { return false; } $lc = SearchEngine::legalSearchChars() . "&#;"; + $db =& wfGetDB( DB_WRITE ); + if( $this->mText == false ) { # Just update the title - $lowpri=$wgIsMySQL?"LOW_PRIORITY":""; + $lowpri = $db->lowPriorityOption(); $sql = "UPDATE $lowpri searchindex SET si_title='" . wfStrencode( Title::indexTitle( $this->mNamespace, $this->mTitle ) ) . "' WHERE si_page={$this->mId}"; - wfQuery( $sql, DB_WRITE, "SearchUpdate::doUpdate" ); + $db->query( $sql, "SearchUpdate::doUpdate" ); return; } @@ -80,7 +82,7 @@ class SearchUpdate { $sql = "REPLACE INTO searchindex (si_page,si_title,si_text) VALUES ({$this->mId},'" . wfStrencode( Title::indexTitle( $this->mNamespace, $this->mTitle ) ) . "','" . wfStrencode( $text ) . "')"; - wfQuery( $sql, DB_WRITE, "SearchUpdate::doUpdate" ); + $db->query( $sql, "SearchUpdate::doUpdate" ); } } diff --git a/includes/Setup.php b/includes/Setup.php index 21d5ef3931..90be866249 100644 --- a/includes/Setup.php +++ b/includes/Setup.php @@ -70,9 +70,10 @@ global $wgUser, $wgLang, $wgOut, $wgTitle; global $wgArticle, $wgDeferredUpdateList, $wgLinkCache; global $wgMemc, $wgMagicWords, $wgMwRedir, $wgDebugLogFile; global $wgMessageCache, $wgUseMemCached, $wgUseDatabaseMessages; -global $wgMsgCacheExpiry, $wgDBname, $wgCommandLineMode; +global $wgMsgCacheExpiry, $wgCommandLineMode; global $wgBlockCache, $wgParserCache, $wgParser, $wgDBConnections; -global $wgLoadBalancer, $wgDBservers, $wgDBloads, $wgDBuser, $wgDBpassword; +global $wgLoadBalancer, $wgDBservers; +global $wgDBserver, $wgDBuser, $wgDBpassword, $wgDBname, $wgDBtype; # Useful debug output if ( $wgCommandLineMode ) { @@ -146,10 +147,16 @@ wfProfileOut( $fname.'-memcached' ); wfProfileIn( $fname.'-database' ); if ( !$wgDBservers ) { - $wgDBservers = array( $wgDBserver ); - $wgDBloads = array( 1 ); + $wgDBservers = array(array( + 'host' => $wgDBserver, + 'user' => $wgDBuser, + 'password' => $wgDBpassword, + 'dbname' => $wgDBname, + 'type' => $wgDBtype, + 'load' => 1 + )); } -$wgLoadBalancer = LoadBalancer::newFromParams( $wgDBservers, $wgDBloads, $wgDBuser, $wgDBpassword, $wgDBname ); +$wgLoadBalancer = LoadBalancer::newFromParams( $wgDBservers ); $wgLoadBalancer->force(0); wfProfileOut( $fname.'-database' ); diff --git a/includes/SiteStatsUpdate.php b/includes/SiteStatsUpdate.php index 900956b639..f8a6bb5931 100644 --- a/includes/SiteStatsUpdate.php +++ b/includes/SiteStatsUpdate.php @@ -15,7 +15,6 @@ class SiteStatsUpdate { function doUpdate() { - global $wgIsMySQL; $a = array(); if ( $this->mViews < 0 ) { $m = "-1"; } @@ -32,10 +31,12 @@ class SiteStatsUpdate { else if ( $this->mGood > 0 ) { $m = "+1"; } else $m = ""; array_push( $a, "ss_good_articles=(ss_good_articles$m)" ); - $lowpri=$wgIsMySQL?"LOW_PRIORITY":""; + + $db =& wfGetDB( DB_WRITE ); + $lowpri = $db->lowPriorityOption(); $sql = "UPDATE $lowpri site_stats SET " . implode ( ",", $a ) . " WHERE ss_row_id=1"; - wfQuery( $sql, DB_WRITE, "SiteStatsUpdate::doUpdate" ); + $db->query( $sql, "SiteStatsUpdate::doUpdate" ); } } diff --git a/includes/Skin.php b/includes/Skin.php index c3dd2e6d8c..e739e97d4f 100644 --- a/includes/Skin.php +++ b/includes/Skin.php @@ -1440,7 +1440,9 @@ class Skin { # Pass a title object, not a title string function makeLinkObj( &$nt, $text= '', $query = '', $trail = '', $prefix = '' ) { - global $wgOut, $wgUser, $wgLoadBalancer; + global $wgOut, $wgUser; + $fname = 'Skin::makeLinkObj'; + if ( $nt->isExternal() ) { $u = $nt->getFullURL(); $link = $nt->getPrefixedURL(); @@ -1467,16 +1469,17 @@ class Skin { } else { $threshold = $wgUser->getOption('stubthreshold') ; if ( $threshold > 0 ) { - $wgLoadBalancer->force(-1); - $res = wfQuery ( "SELECT LENGTH(cur_text) AS x, cur_namespace, cur_is_redirect FROM cur WHERE cur_id='{$aid}'", DB_READ ) ; - $wgLoadBalancer->force(0); - if ( wfNumRows( $res ) > 0 ) { - $s = wfFetchObject( $res ); + $s = $dbr->getArray( 'cur', + array( 'LENGTH(cur_text) AS x', 'cur_namespace', 'cur_is_redirect' ), + array( 'cur_id' => $aid ), + $fname + ); + + if ( $s !== false ) { $size = $s->x; if ( $s->cur_is_redirect OR $s->cur_namespace != 0 ) { $size = $threshold*2 ; # Really big } - wfFreeResult( $res ); } else { $size = $threshold*2 ; # Really big } diff --git a/includes/SkinPHPTal.php b/includes/SkinPHPTal.php index 2a098c34c2..b7bb276a41 100644 --- a/includes/SkinPHPTal.php +++ b/includes/SkinPHPTal.php @@ -43,7 +43,9 @@ // interpolate variables while (preg_match('/\$([0-9]*?)/sm', $value, $m)) { list($src, $var) = $m; - $varValue = @$this->_context[$var]; + wfSuppressWarnings(); + $varValue = $this->_context[$var]; + wfRestoreWarnings(); $value = str_replace($src, $varValue, $value); } return $value; diff --git a/includes/SpecialAllpages.php b/includes/SpecialAllpages.php index 1cdeaa7de5..27b2b3b0fe 100644 --- a/includes/SpecialAllpages.php +++ b/includes/SpecialAllpages.php @@ -2,12 +2,10 @@ function wfSpecialAllpages( $par=NULL ) { - global $indexMaxperpage, $wgRequest, $wgLoadBalancer; + global $indexMaxperpage, $wgRequest; $indexMaxperpage = 480; $from = $wgRequest->getVal( 'from' ); - $wgLoadBalancer->force(-1); - if( $par ) { indexShowChunk( $par ); } elseif( !is_null( $from ) ) { @@ -15,8 +13,6 @@ function wfSpecialAllpages( $par=NULL ) } else { indexShowToplevel(); } - - $wgLoadBalancer->force(0); } function indexShowToplevel() diff --git a/includes/SpecialAncientpages.php b/includes/SpecialAncientpages.php index 3aa93a54a4..69d2d43eb8 100644 --- a/includes/SpecialAncientpages.php +++ b/includes/SpecialAncientpages.php @@ -13,8 +13,8 @@ class AncientPagesPage extends QueryPage { } function getSQL() { - global $wgIsMySQL; - $use_index=$wgIsMySQL?"USE INDEX (cur_timestamp)":""; + $db = wfGetDB( DB_READ ); + $use_index = $db->useIndexClause( 'cur_timestamp' ); return "SELECT 'Ancientpages' as type, cur_namespace as namespace, diff --git a/includes/SpecialContributions.php b/includes/SpecialContributions.php index 0dcd5ebfc3..5fd831ae49 100644 --- a/includes/SpecialContributions.php +++ b/includes/SpecialContributions.php @@ -2,7 +2,7 @@ function wfSpecialContributions( $par = "" ) { - global $wgUser, $wgOut, $wgLang, $wgRequest, $wgIsPg; + global $wgUser, $wgOut, $wgLang, $wgRequest; $fname = "wfSpecialContributions"; $sysop = $wgUser->isSysop(); @@ -65,7 +65,7 @@ function wfSpecialContributions( $par = "" ) "&offset={$offset}&limit={$limit}&hideminor=1" ); } - $oldtable=$wgIsPg?'"old"':'old'; + $oldtable = wfTableName( 'old', DB_READ ); if ( $userCond == "" ) { $sql = "SELECT cur_namespace,cur_title,cur_timestamp,cur_comment,cur_minor_edit,cur_is_new,cur_user_text FROM cur " . "WHERE cur_user_text='" . wfStrencode( $nt->getText() ) . "' {$cmq} " . diff --git a/includes/SpecialListadmins.php b/includes/SpecialListadmins.php index 8518c43eaf..ba5a579b76 100644 --- a/includes/SpecialListadmins.php +++ b/includes/SpecialListadmins.php @@ -16,8 +16,7 @@ class ListAdminsPage extends PageQueryPage { } function getSQL() { - global $wgIsPg; - $usertable = $wgIsPg?'"user"':'user'; + $usertable = wfTableName( 'user' ); $userspace = Namespace::getUser(); return 'SELECT user_rights as type,'.$userspace.' as namespace,'. 'user_name as title, user_name as value '. diff --git a/includes/SpecialListusers.php b/includes/SpecialListusers.php index e74f2a23a9..5a317f2a02 100644 --- a/includes/SpecialListusers.php +++ b/includes/SpecialListusers.php @@ -13,8 +13,7 @@ class ListUsersPage extends QueryPage { } function getSQL() { - global $wgIsPg; - $usertable = $wgIsPg?'"user"':'user'; + $usertable = wfTableName( 'user', DB_READ ); $userspace = Namespace::getUser(); return "SELECT user_rights as type, $userspace as namespace, user_name as title, user_name as value FROM $usertable"; } @@ -36,7 +35,7 @@ class ListUsersPage extends QueryPage { } function wfSpecialListusers() { - global $wgUser, $wgOut, $wgLang, $wgIsPg; + global $wgUser, $wgOut, $wgLang; list( $limit, $offset ) = wfCheckLimits(); diff --git a/includes/SpecialMaintenance.php b/includes/SpecialMaintenance.php index 3b16d6a287..6e3f2d0aa5 100644 --- a/includes/SpecialMaintenance.php +++ b/includes/SpecialMaintenance.php @@ -10,46 +10,27 @@ function sns() function wfSpecialMaintenance( $par=NULL ) { global $wgUser, $wgOut, $wgLang, $wgTitle, $wgRequest, $wgLanguageCode; - global $wgMiserMode, $wgLoadBalancer; + global $wgMiserMode; if ( $wgMiserMode ) { $wgOut->addWikiText( wfMsg( "perfdisabled" ) ); return; } - $wgLoadBalancer->force(-1); - $submitmll = $wgRequest->getVal( 'submitmll' ); - + if( $par ) $subfunction = $par; else $subfunction = $wgRequest->getText( 'subfunction' ); - $done = true; - - if ( $subfunction == "disambiguations" ) { - wfSpecialDisambiguations() ; - } elseif ( $subfunction == "doubleredirects" ) { - wfSpecialDoubleRedirects() ; - } elseif ( $subfunction == "brokenredirects" ) { - wfSpecialBrokenRedirects() ; - } elseif ( $subfunction == "selflinks" ) { - wfSpecialSelfLinks() ; - } elseif ( $subfunction == "mispeelings" ) { - wfSpecialMispeelings() ; - } elseif ( $subfunction == "missinglanguagelinks" ) { - wfSpecialMissingLanguageLinks() ; - } elseif ( !is_null( $submitmll ) ) { - wfSpecialMissingLanguageLinks() ; - } else { - $done = false; - } - - $wgLoadBalancer->force(0); - if ( $done ) { - return; - } + if ( $subfunction == "disambiguations" ) return wfSpecialDisambiguations() ; + if ( $subfunction == "doubleredirects" ) return wfSpecialDoubleRedirects() ; + if ( $subfunction == "brokenredirects" ) return wfSpecialBrokenRedirects() ; + if ( $subfunction == "selflinks" ) return wfSpecialSelfLinks() ; + if ( $subfunction == "mispeelings" ) return wfSpecialMispeelings() ; + if ( $subfunction == "missinglanguagelinks" ) return wfSpecialMissingLanguageLinks() ; + if ( !is_null( $submitmll ) ) return wfSpecialMissingLanguageLinks() ; $sk = $wgUser->getSkin(); $ns = $wgLang->getNamespaces() ; @@ -59,7 +40,7 @@ function wfSpecialMaintenance( $par=NULL ) $r .= "
    • ".getMPL("doubleredirects")."
    • \n" ; $r .= "
    • ".getMPL("brokenredirects")."
    • \n" ; $r .= "
    • ".getMPL("selflinks")."
    • \n" ; - $r .= "
    • ".getMPL("mispeelings")."
    • \n" ; + $r .= "
    • ".getMPL("mispeelings")."
    • \n" ; $r .= "
    • "; $l = getMPL("missinglanguagelinks"); @@ -77,7 +58,7 @@ function wfSpecialMaintenance( $par=NULL ) foreach ( $ak AS $k ) { if ( $k != $wgLanguageCode ) $r .= "\n" ; - } + } $r .= "\n" ; $r .= "\n
    • " ; @@ -278,69 +259,69 @@ function wfSpecialSelfLinks() function wfSpecialMispeelings () { - global $wgUser, $wgOut, $wgLang, $wgTitle; - $sk = $wgUser->getSkin(); - $fname = "wfSpecialMispeelings"; - - list( $limit, $offset ) = wfCheckLimits(); - - # Determine page name - $ms = wfMsg ( "mispeelingspage" ) ; - $mss = wfStrencode( str_replace ( " " , "_" , $ms ) ); - $msp = $wgLang->getNsText(4).":".$ms ; - $msl = $sk->makeKnownLink ( $msp ) ; - - # Load list from database - $sql = "SELECT cur_text FROM cur WHERE cur_title='{$mss}' AND cur_namespace=4" ; - $res = wfQuery( $sql, DB_READ, $fname ); - $obj = wfFetchObject ( $res ) ; - $l = $obj->cur_text ; - $l = explode ( "\n" , $l ) ; - $a = array () ; - foreach ( $l as $x ) - if ( substr ( trim ( $x ) , 0 , 1 ) == "*" ) - $a[] = strtolower ( trim ( substr ( trim ( $x ) , 1 ) ) ); - asort ( $a ) ; - - $cnt = 0 ; - $b = array () ; - foreach ( $a AS $x ) { - if ( $cnt < $offset+$limit && $x != "" ) { - $y = $x ; - $x = preg_replace( '/^(\S+).*$/', '$1', $x ); + global $wgUser, $wgOut, $wgLang, $wgTitle; + $sk = $wgUser->getSkin(); + $fname = "wfSpecialMispeelings"; + + list( $limit, $offset ) = wfCheckLimits(); + + # Determine page name + $ms = wfMsg ( "mispeelingspage" ) ; + $mss = wfStrencode( str_replace ( " " , "_" , $ms ) ); + $msp = $wgLang->getNsText(4).":".$ms ; + $msl = $sk->makeKnownLink ( $msp ) ; + + # Load list from database + $sql = "SELECT cur_text FROM cur WHERE cur_title='{$mss}' AND cur_namespace=4" ; + $res = wfQuery( $sql, DB_READ, $fname ); + $obj = wfFetchObject ( $res ) ; + $l = $obj->cur_text ; + $l = explode ( "\n" , $l ) ; + $a = array () ; + foreach ( $l as $x ) + if ( substr ( trim ( $x ) , 0 , 1 ) == "*" ) + $a[] = strtolower ( trim ( substr ( trim ( $x ) , 1 ) ) ); + asort ( $a ) ; + + $cnt = 0 ; + $b = array () ; + foreach ( $a AS $x ) { + if ( $cnt < $offset+$limit && $x != "" ) { + $y = $x ; + $x = preg_replace( '/^(\S+).*$/', '$1', $x ); #$sql = "SELECT DISTINCT cur_title FROM cur WHERE cur_namespace=0 AND cur_is_redirect=0 AND (MATCH(cur_ind_text) AGAINST ('" . wfStrencode( $wgLang->stripForSearch( $x ) ) . "'))" ; $sql = "SELECT DISTINCT cur_title FROM cur,searchindex WHERE cur_id=si_page AND cur_namespace=0 AND cur_is_redirect=0 AND (MATCH(si_text) AGAINST ('" . wfStrencode( $wgLang->stripForSearch( $x ) ) . "'))" ; - $res = wfQuery( $sql, DB_READ, $fname ); - while ( $obj = wfFetchObject ( $res ) ) { - if ( $cnt >= $offset AND $cnt < $offset+$limit ) { - if ( $y != "" ) { - if ( count ( $b ) > 0 ) $b[] = "\n" ; - $b[] = "

      {$y}

      \n
        \n" ; - $y = "" ; - } - $b[] = "
      1. ". - $sk->makeKnownLink ( $obj->cur_title ). - " (". - $sk->makeBrokenLink ( $obj->cur_title , wfMsg ( "qbedit" ) ). - ")
      2. \n" ; - } - $cnt++ ; - } - } - } - $top = getMaintenancePageBacklink( "mispeelings" ); - $top .= "

        ".wfMsg( "mispeelingstext", $msl )."


        \n"; - $top .= wfShowingResults( $offset, $limit ); - $wgOut->addHTML( "

        {$top}\n" ); - - $sl = wfViewPrevNext( $offset, $limit, "REPLACETHIS" ) ; - $sl = str_replace ( "REPLACETHIS" , sns().":Maintenance&subfunction=mispeelings" , $sl ) ; - $wgOut->addHTML( "
        {$sl}\n" ); - - $s = implode ( "" , $b ) ; - if ( count ( $b ) > 0 ) $s .= "

      "; - $wgOut->addHTML( $s ); - $wgOut->addHTML( "

      {$sl}\n" ); + $res = wfQuery( $sql, DB_READ, $fname ); + while ( $obj = wfFetchObject ( $res ) ) { + if ( $cnt >= $offset AND $cnt < $offset+$limit ) { + if ( $y != "" ) { + if ( count ( $b ) > 0 ) $b[] = "\n" ; + $b[] = "

      {$y}

      \n
        \n" ; + $y = "" ; + } + $b[] = "
      1. ". + $sk->makeKnownLink ( $obj->cur_title ). + " (". + $sk->makeBrokenLink ( $obj->cur_title , wfMsg ( "qbedit" ) ). + ")
      2. \n" ; + } + $cnt++ ; + } + } + } + $top = getMaintenancePageBacklink( "mispeelings" ); + $top .= "

        ".wfMsg( "mispeelingstext", $msl )."


        \n"; + $top .= wfShowingResults( $offset, $limit ); + $wgOut->addHTML( "

        {$top}\n" ); + + $sl = wfViewPrevNext( $offset, $limit, "REPLACETHIS" ) ; + $sl = str_replace ( "REPLACETHIS" , sns().":Maintenance&subfunction=mispeelings" , $sl ) ; + $wgOut->addHTML( "
        {$sl}\n" ); + + $s = implode ( "" , $b ) ; + if ( count ( $b ) > 0 ) $s .= "

      "; + $wgOut->addHTML( $s ); + $wgOut->addHTML( "

      {$sl}\n" ); } diff --git a/includes/SpecialNewpages.php b/includes/SpecialNewpages.php index 77940e96eb..095bb7f77d 100644 --- a/includes/SpecialNewpages.php +++ b/includes/SpecialNewpages.php @@ -14,7 +14,7 @@ class NewPagesPage extends QueryPage { #return parent::isExpensive(); } - function getSQL( $offset, $limit ) { + function getSQL() { return "SELECT 'Newpages' as type, rc_namespace AS namespace, diff --git a/includes/SpecialRandompage.php b/includes/SpecialRandompage.php index c795670f1d..0dd4b60487 100644 --- a/includes/SpecialRandompage.php +++ b/includes/SpecialRandompage.php @@ -3,14 +3,15 @@ function wfSpecialRandompage() { - global $wgOut, $wgTitle, $wgArticle, $wgIsMySQL, $wgExtraRandompageSQL; + global $wgOut, $wgTitle, $wgArticle, $wgExtraRandompageSQL; $fname = "wfSpecialRandompage"; wfSeedRandom(); $rand = mt_rand() / mt_getrandmax(); # interpolation and sprintf() can muck up with locale-specific decimal separator $randstr = number_format( $rand, 12, ".", "" ); - $use_index=$wgIsMySQL?"USE INDEX (cur_random)":""; + $db =& wfGetDB( DB_READ ); + $use_index = $db->useIndexClause( 'cur_random' ); if ( $wgExtraRandompageSQL ) { $extra = "AND ($wgExtraRandompageSQL)"; } else { @@ -22,8 +23,8 @@ function wfSpecialRandompage() AND cur_random>$randstr ORDER BY cur_random LIMIT 1"; - $res = wfQuery( $sqlget, DB_READ, $fname ); - if( $s = wfFetchObject( $res ) ) { + $res = $db->query( $sqlget, $fname ); + if( $s = $db->fetchObject( $res ) ) { $rt = wfUrlEncode( $s->cur_title ); } else { # No articles?! diff --git a/includes/SpecialStatistics.php b/includes/SpecialStatistics.php index f53cebc4ea..883ef0fc71 100644 --- a/includes/SpecialStatistics.php +++ b/includes/SpecialStatistics.php @@ -2,22 +2,22 @@ function wfSpecialStatistics() { - global $wgUser, $wgOut, $wgLang, $wgIsPg, $wgLoadBalancer; + global $wgUser, $wgOut, $wgLang; $fname = "wfSpecialStatistics"; - $wgLoadBalancer->force(-1); - $wgOut->addHTML( "

      " . wfMsg( "sitestats" ) . "

      \n" ); + + $db =& wfGetDB( DB_READ ); $sql = "SELECT COUNT(cur_id) AS total FROM cur"; - $res = wfQuery( $sql, DB_READ, $fname ); - $row = wfFetchObject( $res ); + $res = $db->query( $sql, $fname ); + $row = $db->fetchObject( $res ); $total = $row->total; $sql = "SELECT ss_total_views, ss_total_edits, ss_good_articles " . "FROM site_stats WHERE ss_row_id=1"; - $res = wfQuery( $sql, DB_READ, $fname ); - $row = wfFetchObject( $res ); + $res = $db->query( $sql, $fname ); + $row = $db->fetchObject( $res ); $views = $row->ss_total_views; $edits = $row->ss_total_edits; $good = $row->ss_good_articles; @@ -33,16 +33,16 @@ function wfSpecialStatistics() $wgOut->addWikiText( $text ); $wgOut->addHTML( "

      " . wfMsg( "userstats" ) . "

      \n" ); - $usertable=$wgIsPg?'"user"':'user'; + $usertable = $db->tableName( 'user' ); $sql = "SELECT COUNT(user_id) AS total FROM $usertable"; - $res = wfQuery( $sql, DB_READ, $fname ); - $row = wfFetchObject( $res ); + $res = $db->query( $sql, $fname ); + $row = $db->fetchObject( $res ); $total = $row->total; $sql = "SELECT COUNT(user_id) AS total FROM $usertable " . "WHERE user_rights LIKE '%sysop%'"; - $res = wfQuery( $sql, DB_READ, $fname ); - $row = wfFetchObject( $res ); + $res = $db->query( $sql, $fname ); + $row = $db->fetchObject( $res ); $admins = $row->total; $sk = $wgUser->getSkin(); @@ -53,7 +53,6 @@ function wfSpecialStatistics() $wgLang->formatNum( $admins ), $ap ); $wgOut->addWikiText( $text ); - $wgLoadBalancer->force(0); } ?> diff --git a/includes/SpecialWatchlist.php b/includes/SpecialWatchlist.php index aece38b200..ba933092bf 100644 --- a/includes/SpecialWatchlist.php +++ b/includes/SpecialWatchlist.php @@ -4,8 +4,8 @@ require_once( "WatchedItem.php" ); function wfSpecialWatchlist() { - global $wgUser, $wgOut, $wgLang, $wgTitle, $wgMemc, $wgLoadBalancer; - global $wgUseWatchlistCache, $wgWLCacheTimeout, $wgDBname, $wgIsMySQL; + global $wgUser, $wgOut, $wgLang, $wgTitle, $wgMemc; + global $wgUseWatchlistCache, $wgWLCacheTimeout, $wgDBname; global $days, $limit, $target; # From query string $fname = "wfSpecialWatchlist"; @@ -51,19 +51,15 @@ function wfSpecialWatchlist() } } - $wgLoadBalancer->force(-1); $sql = "SELECT COUNT(*) AS n FROM watchlist WHERE wl_user=$uid"; $res = wfQuery( $sql, DB_READ ); $s = wfFetchObject( $res ); $nitems = $s->n; - $wgLoadBalancer->force(0); if($nitems == 0) { $wgOut->addHTML( wfMsg( "nowatchlist" ) ); return; } - $wgLoadBalancer->force(-1); - if ( ! isset( $days ) ) { $big = 1000; if($nitems > $big) { @@ -119,7 +115,6 @@ function wfSpecialWatchlist() wfMsg( "removechecked" ) . "' />\n" . "\n" ); - $wgLoadBalancer->force(0); return; } @@ -145,7 +140,7 @@ function wfSpecialWatchlist() $wgLang->formatNum( $nitems ), $wgLang->formatNum( $npages ), $y, $specialTitle->escapeLocalUrl( "magic=yes" ) ) . "
      \n" ); - $use_index=$wgIsMySQL?"USE INDEX ($x)":""; + $use_index = wfUseIndexClause( $x, DB_READ ); $sql = "SELECT cur_namespace,cur_title,cur_comment, cur_id, cur_user,cur_user_text,cur_timestamp,cur_minor_edit,cur_is_new @@ -171,7 +166,6 @@ function wfSpecialWatchlist() if ( wfNumRows( $res ) == 0 ) { $wgOut->addHTML( "

      " . wfMsg( "watchnochange" ) . "

      " ); - $wgLoadBalancer->force(0); return; } @@ -193,7 +187,6 @@ function wfSpecialWatchlist() $wgMemc->set( $memckey, $s, $wgWLCacheTimeout); } - $wgLoadBalancer->force(0); } diff --git a/includes/Title.php b/includes/Title.php index 7acdc61cdd..14728c8338 100644 --- a/includes/Title.php +++ b/includes/Title.php @@ -88,7 +88,7 @@ class Title { # From a URL-encoded title /* static */ function newFromURL( $url ) { - global $wgLang, $wgServer, $wgIsMySQL, $wgIsPg; + global $wgLang, $wgServer; $t = new Title(); # For compatibility with old buggy URLs. "+" is not valid in titles, @@ -109,20 +109,10 @@ class Title { $t->mDbkeyform = str_replace( " ", "_", $s ); if( $t->secureAndSplit() ) { - # check that lenght of title is < cur_title size - if ($wgIsMySQL) { - $sql = "SHOW COLUMNS FROM cur LIKE \"cur_title\";"; - $cur_title_object = wfFetchObject(wfQuery( $sql, DB_READ )); - - preg_match( "/\((.*)\)/", $cur_title_object->Type, $cur_title_type); - $cur_title_size=$cur_title_type[1]; - } else { - /* midom:FIXME pg_field_type does not return varchar length - assume 255 */ - $cur_title_size=255; - } - - if (strlen($t->mDbkeyform) > $cur_title_size ) { + # check that length of title is < cur_title size + $dbr =& wfGetDB( DB_READ ); + $maxSize = $dbr->textFieldSize( 'cur', 'cur_title' ); + if ( $maxSize != -1 && strlen( $t->mDbkeyform ) > $maxSize ) { return NULL; } @@ -138,7 +128,8 @@ class Title { /* static */ function newFromID( $id ) { $fname = "Title::newFromID"; - $row = wfGetArray( "cur", array( "cur_namespace", "cur_title" ), + $dbr =& wfGetDB( DB_READ ); + $row = $dbr->getArray( "cur", array( "cur_namespace", "cur_title" ), array( "cur_id" => $id ), $fname ); if ( $row !== false ) { $title = Title::makeTitle( $row->cur_namespace, $row->cur_title ); @@ -172,12 +163,12 @@ class Title { # Get the prefixed DB key associated with an ID /* static */ function nameOf( $id ) { - $sql = "SELECT cur_namespace,cur_title FROM cur WHERE " . - "cur_id={$id}"; - $res = wfQuery( $sql, DB_READ, "Article::nameOf" ); - if ( 0 == wfNumRows( $res ) ) { return NULL; } + $fname = 'Title::nameOf'; + $dbr =& wfGetDB( DB_READ ); + + $s = $dbr->getArray( 'cur', array( 'cur_namespace','cur_title' ), array( 'cur_id' => $id ), $fname ); + if ( $s === false ) { return NULL; } - $s = wfFetchObject( $res ); $n = Title::makeName( $s->cur_namespace, $s->cur_title ); return $n; } @@ -244,8 +235,7 @@ class Title { function getInterwikiLink( $key ) { global $wgMemc, $wgDBname, $wgInterwikiExpiry, $wgTitleInterwikiCache; - global $wgLoadBalancer; - + $fname = 'Title::getInterwikiLink'; $k = "$wgDBname:interwiki:$key"; if( array_key_exists( $k, $wgTitleInterwikiCache ) ) @@ -257,17 +247,16 @@ class Title { $wgTitleInterwikiCache[$k] = $s; return $s->iw_url; } - $dkey = wfStrencode( $key ); - $wgLoadBalancer->force(-1); - $query = "SELECT iw_url,iw_local FROM interwiki WHERE iw_prefix='$dkey'"; - $res = wfQuery( $query, DB_READ, "Title::getInterwikiLink" ); - $wgLoadBalancer->force(0); + $dbr =& wfGetDB( DB_READ ); + $res = $dbr->select( 'interwiki', array( 'iw_url', 'iw_local' ), array( 'iw_prefix' => $key ), $fname ); if(!$res) return ""; - $s = wfFetchObject( $res ); + $s = $dbr->fetchObject( $res ); if(!$s) { + # Cache non-existence: create a blank object and save it to memcached $s = (object)false; $s->iw_url = ""; + $s->iw_local = 0; } $wgMemc->set( $k, $s, $wgInterwikiExpiry ); $wgTitleInterwikiCache[$k] = $s; @@ -296,20 +285,21 @@ class Title { if ( $timestamp == "" ) { $timestamp = wfTimestampNow(); } - $sql = "UPDATE cur SET cur_touched='{$timestamp}' WHERE cur_id IN ("; + $dbw =& wfGetDB( DB_WRITE ); + $cur = $dbw->tableName( 'cur' ); + $sql = "UPDATE $cur SET cur_touched='{$timestamp}' WHERE cur_id IN ("; $first = true; foreach ( $titles as $title ) { if ( ! $first ) { $sql .= ","; } - $first = false; $sql .= $title->getArticleID(); } $sql .= ")"; if ( ! $first ) { - wfQuery( $sql, DB_WRITE, "Title::touchArray" ); + $dbw->query( $sql, "Title::touchArray" ); } } @@ -568,7 +558,8 @@ class Title { if ( 0 == $id ) { return array(); } if ( ! $this->mRestrictionsLoaded ) { - $res = wfGetSQL( "cur", "cur_restrictions", "cur_id=$id" ); + $dbr =& wfGetDB( DB_READ ); + $res = $dbr->getField( "cur", "cur_restrictions", "cur_id=$id" ); $this->mRestrictions = explode( ",", trim( $res ) ); $this->mRestrictionsLoaded = true; } @@ -576,15 +567,13 @@ class Title { } # Is there a version of this page in the deletion archive? + # Returns the number of archived revisions function isDeleted() { - $ns = $this->getNamespace(); - $t = wfStrencode( $this->getDBkey() ); - $sql = "SELECT COUNT(*) AS n FROM archive WHERE ar_namespace=$ns AND ar_title='$t'"; - if( $res = wfQuery( $sql, DB_READ ) ) { - $s = wfFetchObject( $res ); - return $s->n; - } - return 0; + $fname = 'Title::isDeleted'; + $dbr =& wfGetDB( DB_READ ); + $n = $dbr->getField( 'archive', 'COUNT(*)', array( 'ar_namespace' => $this->getNamespace(), + 'ar_title' => $this->getDBkey() ), $fname ); + return (int)$n; } # Get the article ID from the link cache @@ -616,10 +605,16 @@ class Title { # Called from LinksUpdate.php function invalidateCache() { $now = wfTimestampNow(); - $ns = $this->getNamespace(); - $ti = wfStrencode( $this->getDBkey() ); - $sql = "UPDATE cur SET cur_touched='$now' WHERE cur_namespace=$ns AND cur_title='$ti'"; - return wfQuery( $sql, DB_WRITE, "Title::invalidateCache" ); + $dbw =& wfGetDB( DB_WRITE ); + $success = $dbw->updateArray( 'cur', + array( /* SET */ + 'cur_touched' => wfTimestampNow() + ), array( /* WHERE */ + 'cur_namespace' => $this->getNamespace() , + 'cur_title' => $this->getDBkey() + ), 'Title::invalidateCache' + ); + return $success; } # Prefixes some arbitrary text with the namespace or interwiki prefix of this object @@ -640,8 +635,8 @@ class Title { # Secure and split - main initialisation function for this object # # Assumes that mDbkeyform has been set, and is urldecoded - # and uses undersocres, but not otherwise munged. This function - # removes illegal characters, splits off the winterwiki and + # and uses underscores, but not otherwise munged. This function + # removes illegal characters, splits off the interwiki and # namespace prefixes, sets the other forms, and canonicalizes # everything. # @@ -733,6 +728,7 @@ class Title { # Reject illegal characters. # if( preg_match( $rxTc, $r ) ) { + wfProfileOut( $fname ); return false; } @@ -743,6 +739,7 @@ class Title { strpos( $r, "/./" !== false ) || strpos( $r, "/../" !== false ) ) ) { + wfProfileOut( $fname ); return false; } @@ -773,21 +770,31 @@ class Title { # Get an array of Title objects linking to this title # Also stores the IDs in the link cache + # $options may be FOR UPDATE function getLinksTo( $options = '' ) { global $wgLinkCache; $id = $this->getArticleID(); - $sql = "SELECT cur_namespace,cur_title,cur_id FROM cur,links WHERE l_from=cur_id AND l_to={$id} $options"; - $res = wfQuery( $sql, DB_READ, "Title::getLinksTo" ); + + if ( $options ) { + $db =& wfGetDB( DB_WRITE ); + } else { + $db =& wfGetDB( DB_READ ); + } + $cur = $db->tableName( 'cur' ); + $links = $db->tableName( 'links' ); + + $sql = "SELECT cur_namespace,cur_title,cur_id FROM $cur,$links WHERE l_from=cur_id AND l_to={$id} $options"; + $res = $db->query( $sql, "Title::getLinksTo" ); $retVal = array(); - if ( wfNumRows( $res ) ) { - while ( $row = wfFetchObject( $res ) ) { + if ( $db->numRows( $res ) ) { + while ( $row = $db->fetchObject( $res ) ) { if ( $titleObj = Title::makeTitle( $row->cur_namespace, $row->cur_title ) ) { $wgLinkCache->addGoodLink( $row->cur_id, $titleObj->getPrefixedDBkey() ); $retVal[] = $titleObj; } } } - wfFreeResult( $res ); + $db->freeResult( $res ); return $retVal; } @@ -795,19 +802,28 @@ class Title { # Also stores the IDs in the link cache function getBrokenLinksTo( $options = '' ) { global $wgLinkCache; - $encTitle = wfStrencode( $this->getPrefixedDBkey() ); - $sql = "SELECT cur_namespace,cur_title,cur_id FROM brokenlinks,cur " . + + if ( $options ) { + $db =& wfGetDB( DB_WRITE ); + } else { + $db =& wfGetDB( DB_READ ); + } + $cur = $db->tableName( 'cur' ); + $brokenlinks = $db->tableName( 'brokenlinks' ); + $encTitle = $db->strencode( $this->getPrefixedDBkey() ); + + $sql = "SELECT cur_namespace,cur_title,cur_id FROM $brokenlinks,$cur " . "WHERE bl_from=cur_id AND bl_to='$encTitle' $options"; - $res = wfQuery( $sql, DB_READ, "Title::getBrokenLinksTo" ); + $res = $db->query( $sql, "Title::getBrokenLinksTo" ); $retVal = array(); - if ( wfNumRows( $res ) ) { - while ( $row = wfFetchObject( $res ) ) { + if ( $db->numRows( $res ) ) { + while ( $row = $db->fetchObject( $res ) ) { $titleObj = Title::makeTitle( $row->cur_namespace, $row->cur_title ); $wgLinkCache->addGoodLink( $row->cur_id, $titleObj->getPrefixedDBkey() ); $retVal[] = $titleObj; } } - wfFreeResult( $res ); + $db->freeResult( $res ); return $retVal; } @@ -896,10 +912,11 @@ class Title { $won = wfInvertTimestamp( $now ); $newid = $nt->getArticleID(); $oldid = $this->getArticleID(); - + $dbw =& wfGetDB( DB_WRITE ); + $links = $dbw->tableName( 'links' ); + # Change the name of the target page: - wfUpdateArray( - /* table */ 'cur', + $dbw->updateArray( 'cur', /* SET */ array( 'cur_touched' => $now, 'cur_namespace' => $nt->getNamespace(), @@ -914,8 +931,7 @@ class Title { # by definition if we've got here it's rather uninteresting. $redirectText = $wgMwRedir->getSynonym( 0 ) . " [[" . $nt->getPrefixedText() . "]]\n"; - wfUpdateArray( - /* table */ 'cur', + $dbw->updateArray( 'cur', /* SET */ array( 'cur_touched' => $now, 'cur_timestamp' => $now, @@ -940,7 +956,7 @@ class Title { # Fix the redundant names for the past revisions of the target page. # The redirect should have no old revisions. - wfUpdateArray( + $dbw->updateArray( /* table */ 'old', /* SET */ array( 'old_namespace' => $nt->getNamespace(), @@ -962,12 +978,12 @@ class Title { $linksToNew = $nt->getLinksTo( 'FOR UPDATE' ); # Delete them all - $sql = "DELETE FROM links WHERE l_to=$oldid OR l_to=$newid"; - wfQuery( $sql, DB_WRITE, $fname ); + $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 "; + $sql = "INSERT INTO $links (l_from,l_to) VALUES "; $first = true; # Insert links to old title @@ -992,15 +1008,13 @@ class Title { $sql .= "($id, $oldid)"; } - wfQuery( $sql, DB_WRITE, $fname ); + $dbw->query( $sql, DB_WRITE, $fname ); } # Now, we record the link from the redirect to the new title. # It should have no other outgoing links... - $sql = "DELETE FROM links WHERE l_from={$newid}"; - wfQuery( $sql, DB_WRITE, $fname ); - $sql = "INSERT INTO links (l_from,l_to) VALUES ({$newid},{$oldid})"; - wfQuery( $sql, DB_WRITE, $fname ); + $dbw->delete( 'links', array( 'l_from' => $newid ) ); + $dbw->insertArray( 'links', array( 'l_from' => $newid, 'l_to' => $oldid ) ); # Clear linkscc LinkCache::linksccClearLinksTo( $oldid ); @@ -1027,10 +1041,10 @@ class Title { $won = wfInvertTimestamp( $now ); $newid = $nt->getArticleID(); $oldid = $this->getArticleID(); + $dbw =& wfGetDB( DB_WRITE ); # Rename cur entry - wfUpdateArray( - /* table */ 'cur', + $dbw->updateArray( 'cur', /* SET */ array( 'cur_touched' => $now, 'cur_namespace' => $nt->getNamespace(), @@ -1043,7 +1057,7 @@ class Title { $wgLinkCache->clearLink( $nt->getPrefixedDBkey() ); # Insert redirct - wfInsertArray( 'cur', array( + $dbw->insertArray( 'cur', array( 'cur_namespace' => $this->getNamespace(), 'cur_title' => $this->getDBkey(), 'cur_comment' => $comment, @@ -1054,13 +1068,13 @@ class Title { 'cur_touched' => $now, 'cur_is_redirect' => 1, 'cur_is_new' => 1, - 'cur_text' => "#REDIRECT [[" . $nt->getPrefixedText() . "]]\n" ) + 'cur_text' => "#REDIRECT [[" . $nt->getPrefixedText() . "]]\n" ), $fname ); - $newid = wfInsertId(); + $newid = $dbw->insertId(); $wgLinkCache->clearLink( $this->getPrefixedDBkey() ); # Rename old entries - wfUpdateArray( + $dbw->updateArray( /* table */ 'old', /* SET */ array( 'old_namespace' => $nt->getNamespace(), @@ -1079,13 +1093,11 @@ class Title { Article::onArticleCreate( $nt ); # Any text links to the old title must be reassigned to the redirect - $sql = "UPDATE links SET l_to={$newid} WHERE l_to={$oldid}"; - wfQuery( $sql, DB_WRITE, $fname ); + $dbw->updateArray( 'links', array( 'l_to' => $newid ), array( 'l_to' => $oldid ), $fname ); LinkCache::linksccClearLinksTo( $oldid ); # Record the just-created redirect's linking to the page - $sql = "INSERT INTO links (l_from,l_to) VALUES ({$newid},{$oldid})"; - wfQuery( $sql, DB_WRITE, $fname ); + $dbw->insertArray( 'links', array( 'l_from' => $newid, 'l_to' => $oldid ), $fname ); # Non-existent target may have had broken links to it; these must # now be removed and made into good links. @@ -1106,19 +1118,18 @@ class Title { } # Checks if $this can be moved to $nt - # Both titles must exist in the database, otherwise it will blow up + # Selects for update, so don't call it unless you mean business function isValidMoveTarget( $nt ) { $fname = "Title::isValidMoveTarget"; + $dbw =& wfGetDB( DB_WRITE ); # Is it a redirect? $id = $nt->getArticleID(); - $sql = "SELECT cur_is_redirect,cur_text FROM cur " . - "WHERE cur_id={$id}"; - $res = wfQuery( $sql, DB_READ, $fname ); - $obj = wfFetchObject( $res ); + $obj = $dbw->getArray( 'cur', array( 'cur_is_redirect','cur_text' ), + array( 'cur_id' => $id ), $fname, 'FOR UPDATE' ); - if ( 0 == $obj->cur_is_redirect ) { + if ( !$obj || 0 == $obj->cur_is_redirect ) { # Not a redirect return false; } @@ -1132,9 +1143,11 @@ class Title { } # Does the article have a history? - $row = wfGetArray( 'old', array( 'old_id' ), array( - 'old_namespace' => $nt->getNamespace(), - 'old_title' => $nt->getDBkey() ) + $row = $dbw->getArray( 'old', array( 'old_id' ), + array( + 'old_namespace' => $nt->getNamespace(), + 'old_title' => $nt->getDBkey() + ), $fname, 'FOR UPDATE' ); # Return true if there was no history @@ -1149,10 +1162,14 @@ class Title { return false; } + $fname = "Title::createRedirect"; + $dbw =& wfGetDB( DB_WRITE ); $now = wfTimestampNow(); $won = wfInvertTimestamp( $now ); + $seqVal = $dbw->nextSequenceValue( 'cur_cur_id_seq' ); - wfInsertArray( 'cur', array( + $dbw->insertArray( 'cur', array( + 'cur_id' => $seqVal, 'cur_namespace' => $this->getNamespace(), 'cur_title' => $this->getDBkey(), 'cur_comment' => $comment, @@ -1164,21 +1181,25 @@ class Title { 'cur_is_redirect' => 1, 'cur_is_new' => 1, 'cur_text' => "#REDIRECT [[" . $dest->getPrefixedText() . "]]\n" - )); - $newid = wfInsertId(); + ), $fname ); + $newid = $dbw->insertId(); $this->resetArticleID( $newid ); # Link table if ( $dest->getArticleID() ) { - wfInsertArray( 'links', array( - 'l_to' => $dest->getArticleID(), - 'l_from' => $newid - )); + $dbw->insertArray( 'links', + array( + 'l_to' => $dest->getArticleID(), + 'l_from' => $newid + ), $fname + ); } else { - wfInsertArray( 'brokenlinks', array( - 'bl_to' => $dest->getPrefixedDBkey(), - 'bl_from' => $newid - )); + $dbw->insertArray( 'brokenlinks', + array( + 'bl_to' => $dest->getPrefixedDBkey(), + 'bl_from' => $newid + ), $fname + ); } Article::onArticleCreate( $this ); @@ -1191,21 +1212,23 @@ class Title { { global $wgLang,$wgUser; - #$titlekey = wfStrencode( $this->getArticleID() ); $titlekey = $this->getArticleId(); $cns = Namespace::getCategory(); $sk =& $wgUser->getSkin(); $parents = array(); - + $dbr =& wfGetDB( DB_READ ); + $cur = $dbr->tableName( 'cur' ); + $categorylinks = $dbr->tableName( 'categorylinks' ); + # get the parents categories of this title from the database - $sql = "SELECT DISTINCT cur_id FROM cur,categorylinks + $sql = "SELECT DISTINCT cur_id FROM $cur,$categorylinks WHERE cl_from='$titlekey' AND cl_to=cur_title AND cur_namespace='$cns' ORDER BY cl_sortkey" ; - $res = wfQuery ( $sql, DB_READ ) ; + $res = $dbr->query ( $sql ) ; - if(wfNumRows($res) > 0) { - while ( $x = wfFetchObject ( $res ) ) $data[] = $x ; - wfFreeResult ( $res ) ; + if($dbr->numRows($res) > 0) { + while ( $x = $dbr->fetchObject ( $res ) ) $data[] = $x ; + $dbr->freeResult ( $res ) ; } else { $data = ''; } diff --git a/includes/ViewCountUpdate.php b/includes/ViewCountUpdate.php index 16976f11b0..470fa69f61 100644 --- a/includes/ViewCountUpdate.php +++ b/includes/ViewCountUpdate.php @@ -12,12 +12,13 @@ class ViewCountUpdate { function doUpdate() { - global $wgDisableCounters, $wgIsMySQL; + global $wgDisableCounters; if ( $wgDisableCounters ) { return; } - $lowpri=$wgIsMySQL?"LOW_PRIORITY":""; + $db =& wfGetDB( DB_WRITE ); + $lowpri = $db->lowPriorityOption(); $sql = "UPDATE $lowpri cur SET cur_counter=(1+cur_counter)," . "cur_timestamp=cur_timestamp WHERE cur_id={$this->mPageID}"; - $res = wfQuery( $sql, DB_WRITE, "ViewCountUpdate::doUpdate" ); + $res = $db->query( $sql, "ViewCountUpdate::doUpdate" ); } } ?> diff --git a/includes/WatchedItem.php b/includes/WatchedItem.php index 23ad2804e1..e2d9506f79 100644 --- a/includes/WatchedItem.php +++ b/includes/WatchedItem.php @@ -36,24 +36,17 @@ class WatchedItem { function addWatch() { - global $wgIsMySQL; + $fname = "WatchedItem::addWatch"; # REPLACE instead of INSERT because occasionally someone # accidentally reloads a watch-add operation. - if ($wgIsMySQL) { - $sql = "REPLACE INTO watchlist (wl_user, wl_namespace,wl_title) ". - "VALUES ($this->id,$this->ns,'$this->eti')"; - $res = wfQuery( $sql, DB_WRITE ); - } else { - $sql = "DELETE FROM watchlist WHERE wl_user=$this->id AND - wl_namespace=$this->ns AND wl_title='$this->eti'"; - wfQuery( $sql, DB_WRITE); - $sql = "INSERT INTO watchlist (wl_user, wl_namespace,wl_title) ". - "VALUES ($this->id,$this->ns,'$this->eti')"; - $res = wfQuery( $sql, DB_WRITE ); - } + $dbw =& wfGetDB( DB_WRITE ); + $dbw->replace( 'watchlist', array(array('wl_user', 'wl_namespace', 'wl_title')), + array( + 'wl_user' => $this->id, + 'wl_namespace' => $this->ns, + 'wl_title' => $this->eti, + ), $fname ); - if( $res === false ) return false; - global $wgMemc; $wgMemc->set( $this->watchkey(), 1 ); return true; diff --git a/index.php b/index.php index 992a28b193..075e721b10 100644 --- a/index.php +++ b/index.php @@ -95,7 +95,8 @@ if ( $search = $wgRequest->getText( 'search' ) ) { $wgArticle = new Article( $wgTitle ); } - wfQuery("BEGIN", DB_WRITE); + $db =& wfGetDB( DB_WRITE ); + $db->query("BEGIN", DB_WRITE); switch( $action ) { case "view": $wgOut->setSquidMaxage( $wgSquidMaxage ); @@ -165,7 +166,7 @@ if ( $search = $wgRequest->getText( 'search' ) ) { default: $wgOut->errorpage( "nosuchaction", "nosuchactiontext" ); } - wfQuery("COMMIT", DB_WRITE); + $db->query("COMMIT"); } $wgOut->output(); diff --git a/install-utils.inc b/install-utils.inc index 040c34dc81..2ee841f459 100644 --- a/install-utils.inc +++ b/install-utils.inc @@ -132,7 +132,8 @@ function dbsource( $fname, $database = false ) { # Obsolete, use Database::fieldExists() function field_exists( $table, $field ) { $fname = "Update script: field_exists"; - $res = wfQuery( "DESCRIBE $table", $fname ); + $db =& wfGetDB( DB_READ ); + $res = $db->query( "DESCRIBE $table", $fname ); $found = false; while ( $row = wfFetchObject( $res ) ) { diff --git a/texvc.phtml b/texvc.phtml index 7a91271852..7f5e1a0472 100644 --- a/texvc.phtml +++ b/texvc.phtml @@ -7,148 +7,160 @@ print "?xml version=\"1.0\" encoding=\"utf-8\"?"; print ">"; ?> + PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> texvc " ?>
      "; + global $wgMathPath; + return "\"".wfEscapeHTML($tex)."\""; } function texvc_cgi_renderMath( $tex ) { - global $wgMathDirectory, $wgTmpDirectory, $wgInputEncoding; - $mf = wfMsg( "math_failure" ); - $munk = wfMsg( "math_unknown_error" ); - - $image = ""; - $outhtml = ""; - $outtex = ""; - - $fname = "texvc_cgi_renderMath"; - - $md5 = md5($tex); - $md5_sql = mysql_escape_string(pack("H32", $md5)); - $sql = "SELECT math_outputhash,math_html_conservativeness,math_html,math_mathml FROM math WHERE math_inputhash = '".$md5_sql."'"; - - $res = wfQuery( $sql, 0, $fname ); - if ( wfNumRows( $res ) == 0 ) - { - $cmd = "./math/texvc ".escapeshellarg($wgTmpDirectory)." ". - escapeshellarg($wgMathDirectory)." ".escapeshellarg($tex)." ".escapeshellarg($wgInputEncoding); - $contents = `$cmd`; - - if (strlen($contents) == 0) - return "

      ".$mf." (".$munk."): ".wfEscapeHTML($tex)."

      "; - $retval = substr ($contents, 0, 1); - - if (($retval == "C") || ($retval == "M") || ($retval == "L")) { - if ($retval == "C") - $conservativeness = 2; - else if ($retval == "M") - $conservativeness = 1; - else - $conservativeness = 0; - $outdata = substr ($contents, 33); - - $i = strpos($outdata, "\000"); - - $outhtml = substr($outdata, 0, $i); - $mathml = substr($outdata, $i+1); - - $sql_html = "'".mysql_escape_string($outhtml)."'"; - $sql_mathml = "'".mysql_escape_string($mathml)."'"; - } else if (($retval == "c") || ($retval == "m") || ($retval == "l")) { - $outhtml = substr ($contents, 33); - if ($retval == "c") - $conservativeness = 2; - else if ($retval == "m") - $conservativeness = 1; - else - $conservativeness = 0; - $sql_html = "'".mysql_escape_string($outhtml)."'"; - $mathml = ''; - $sql_mathml = 'NULL'; - } else if ($retval == "X") { - $outhtml = ''; - $mathml = substr ($contents, 33); - $sql_html = 'NULL'; - $sql_mathml = "'".mysql_escape_string($mathml)."'"; - $conservativeness = 0; - } else if ($retval == "+") { - $outhtml = ''; - $mathml = ''; - $sql_html = 'NULL'; - $sql_mathml = 'NULL'; - $conservativeness = 0; + global $wgMathDirectory, $wgTmpDirectory, $wgInputEncoding; + $dbr =& wfGetDB( DB_READ ); + $mf = wfMsg( "math_failure" ); + $munk = wfMsg( "math_unknown_error" ); + + $image = ""; + $outhtml = ""; + $outtex = ""; + + $fname = "texvc_cgi_renderMath"; + + $md5 = md5($tex); + $md5_sql = pack("H32", $md5); + + $mathTable = $dbr->tableName( 'math' ); + $rpage = $dbr->getArray( 'math', array('math_outputhash','math_html_conservativeness','math_html','math_mathml'), + array( 'math_inputhash' => $md5_sql ) ); + + if ( $rpage === false ) + { + $cmd = "./math/texvc ".wfEscapeShellArg($wgTmpDirectory)." ". + wfEscapeShellArg($wgMathDirectory)." ".wfEscapeShellArg($tex)." ".wfEscapeShellArg($wgInputEncoding); + $contents = `$cmd`; + + if (strlen($contents) == 0) + return "

      ".$mf." (".$munk."): ".wfEscapeHTML($tex)."

      "; + $retval = substr ($contents, 0, 1); + + if (($retval == "C") || ($retval == "M") || ($retval == "L")) { + if ($retval == "C") + $conservativeness = 2; + else if ($retval == "M") + $conservativeness = 1; + else + $conservativeness = 0; + $outdata = substr ($contents, 33); + + $i = strpos($outdata, "\000"); + + $outhtml = substr($outdata, 0, $i); + $mathml = substr($outdata, $i+1); + + #$sql_html = "'".mysql_escape_string($outhtml)."'"; + #$sql_mathml = "'".mysql_escape_string($mathml)."'"; + } else if (($retval == "c") || ($retval == "m") || ($retval == "l")) { + $outhtml = substr ($contents, 33); + if ($retval == "c") + $conservativeness = 2; + else if ($retval == "m") + $conservativeness = 1; + else + $conservativeness = 0; + #$sql_html = "'".mysql_escape_string($outhtml)."'"; + $mathml = NULL; + #$sql_mathml = 'NULL'; + } else if ($retval == "X") { + $outhtml = NULL; + $mathml = substr ($contents, 33); + #$sql_html = 'NULL'; + #$sql_mathml = "'".mysql_escape_string($mathml)."'"; + $conservativeness = 0; + } else if ($retval == "+") { + $outhtml = NULL; + $mathml = NULL; + #$sql_html = 'NULL'; + #$sql_mathml = 'NULL'; + $conservativeness = 0; + } else { + if ($retval == "E") + $errmsg = wfMsg( "math_lexing_error" ); + else if ($retval == "S") + $errmsg = wfMsg( "math_syntax_error" ); + else if ($retval == "F") + $errmsg = wfMsg( "math_unknown_function" ); + else + $errmsg = $munk; + return "

      ".$mf." (".$errmsg.substr($contents, 1)."): ".wfEscapeHTML($tex)."

      "; + } + + $outmd5 = substr ($contents, 1, 32); + if (!preg_match("/^[a-f0-9]{32}$/", $outmd5)) + return "

      ".$mf." (".$munk."): ".wfEscapeHTML($tex)."

      "; + + $outmd5_sql = pack("H32", $outmd5); + + # Someone may have inserted the same hash since the SELECT, but that's no big deal, just ignore errors + $dbw =& wfGetDB( DB_WRITE ); + $dbw->insertArray( 'math', + array( + 'math_inputhash' => $md5_sql, + 'math_outputhash' => $outmd5_sql, + 'math_html_conservativeness' => $conservativeness, + 'math_html' => $outhtml, + 'math_mathml' => $mathml, + ), $fname, array( 'IGNORE' ) + ); + +// we don't really care if it fails } else { - if ($retval == "E") - $errmsg = wfMsg( "math_lexing_error" ); - else if ($retval == "S") - $errmsg = wfMsg( "math_syntax_error" ); - else if ($retval == "F") - $errmsg = wfMsg( "math_unknown_function" ); - else - $errmsg = $munk; - return "

      ".$mf." (".$errmsg.substr($contents, 1)."): ".wfEscapeHTML($tex)."

      "; + $rpage = $dbr->fetchObject ( $res ); + $outmd5 = unpack ("H32md5", $rpage->math_outputhash); + $outmd5 = $outmd5 ['md5']; + $outhtml = $rpage->math_html; + $conservativeness = $rpage->math_html_conservativeness; + $mathml = $rpage->math_mathml; } - - $outmd5 = substr ($contents, 1, 32); - if (!preg_match("/^[a-f0-9]{32}$/", $outmd5)) - return "

      ".$mf." (".$munk."): ".wfEscapeHTML($tex)."

      "; - - $outmd5_sql = mysql_escape_string(pack("H32", $outmd5)); - - $sql = "INSERT INTO math VALUES ('".$md5_sql."', '".$outmd5_sql."', ".$conservativeness.", ".$sql_html.", ".$sql_mathml.")"; - - $res = wfQuery( $sql, 0, $fname ); -// we don't really care if it fails - } else { - $rpage = wfFetchObject ( $res ); - $outmd5 = unpack ("H32md5", $rpage->math_outputhash); - $outmd5 = $outmd5 ['md5']; - $outhtml = $rpage->math_html; - $conservativeness = $rpage->math_html_conservativeness; - $mathml = $rpage->math_mathml; - } - if ($mathml == '') - $mathml = "

      Failed to generate MathML

      "; - else - $mathml = "

      MathML

      $mathml"; - $image = "

      Image

      " . xlinkToMathImage ( $tex, $outmd5 ); - $cmd = "./math/texvc_tex ".escapeshellarg($tex)." ".escapeshellarg($wgInputEncoding); - $outtex = `$cmd`; - - if ( $outhtml == '' ) - $outhtml = "

      Failed to generate HTML

      "; - else - if ( $conservativeness == 2) - $outhtml = "

      HTML (conservative)

      " . $outhtml; - else if ( $conservativeness == 1) - $outhtml = "

      HTML (moderate)

      " . $outhtml; + if ( is_null( $mathml ) || $mathml === '' ) + $mathml = "

      Failed to generate MathML

      "; else - $outhtml = "

      HTML (liberal)

      " . $outhtml; + $mathml = "

      MathML

      $mathml"; + $image = "

      Image

      " . xlinkToMathImage ( $tex, $outmd5 ); + $cmd = "./math/texvc_tex ".escapeshellarg($tex)." ".escapeshellarg($wgInputEncoding); + $outtex = `$cmd`; - if ( $outtex == '' ) - $outtex = "

      Failed to generate TeX

      "; - else - $outtex = "

      TeX

      " . wfEscapeHTML($outtex); + if ( is_null( $outhtml ) || $outhtml === '' ) + $outhtml = "

      Failed to generate HTML

      "; + else + if ( $conservativeness == 2) + $outhtml = "

      HTML (conservative)

      " . $outhtml; + else if ( $conservativeness == 1) + $outhtml = "

      HTML (moderate)

      " . $outhtml; + else + $outhtml = "

      HTML (liberal)

      " . $outhtml; + + if ( $outtex == '' ) + $outtex = "

      Failed to generate TeX

      "; + else + $outtex = "

      TeX

      " . wfEscapeHTML($outtex); - return $outtex . $outhtml . $mathml . $image; + return $outtex . $outhtml . $mathml . $image; } global $math; if ($math != '') - print texvc_cgi_renderMath($math); + print texvc_cgi_renderMath($math); ?> -- 2.20.1