summaryrefslogtreecommitdiff
path: root/includes/db/Database.php
diff options
context:
space:
mode:
Diffstat (limited to 'includes/db/Database.php')
-rw-r--r--includes/db/Database.php248
1 files changed, 176 insertions, 72 deletions
diff --git a/includes/db/Database.php b/includes/db/Database.php
index 84b88643..52a59c11 100644
--- a/includes/db/Database.php
+++ b/includes/db/Database.php
@@ -26,6 +26,7 @@ class Database {
#------------------------------------------------------------------------------
protected $mLastQuery = '';
+ protected $mDoneWrites = false;
protected $mPHPError = false;
protected $mServer, $mUser, $mPassword, $mConn = null, $mDBname;
@@ -210,7 +211,14 @@ class Database {
* @return String
*/
function lastQuery() { return $this->mLastQuery; }
-
+
+
+ /**
+ * Returns true if the connection may have been used for write queries.
+ * Should return true if unsure.
+ */
+ function doneWrites() { return $this->mDoneWrites; }
+
/**
* Is a connection to the database open?
* @return Boolean
@@ -493,6 +501,14 @@ class Database {
}
/**
+ * Determine whether a query writes to the DB.
+ * Should return true if unsure.
+ */
+ function isWriteQuery( $sql ) {
+ return !preg_match( '/^(?:SELECT|BEGIN|COMMIT|SET|SHOW)\b/i', $sql );
+ }
+
+ /**
* Usually aborts on failure. If errors are explicitly ignored, returns success.
*
* @param $sql String: SQL query
@@ -527,6 +543,11 @@ class Database {
}
$this->mLastQuery = $sql;
+ if ( !$this->mDoneWrites && $this->isWriteQuery( $sql ) ) {
+ // Set a flag indicating that writes have been done
+ wfDebug( __METHOD__.": Writes done: $sql\n" );
+ $this->mDoneWrites = true;
+ }
# Add a comment for easy SHOW PROCESSLIST interpretation
#if ( $fname ) {
@@ -566,11 +587,15 @@ class Database {
}
}
+ if ( istainted( $sql ) & TC_MYSQL ) {
+ throw new MWException( 'Tainted query found' );
+ }
+
# Do the query and handle errors
$ret = $this->doQuery( $commentedSql );
# Try reconnecting if the connection was lost
- if ( false === $ret && ( $this->lastErrno() == 2013 || $this->lastErrno() == 2006 ) ) {
+ if ( false === $ret && $this->wasErrorReissuable() ) {
# Transaction is gone, like it or not
$this->mTrxLevel = 0;
wfDebug( "Connection lost, reconnecting...\n" );
@@ -1191,6 +1216,7 @@ class Database {
# SHOW INDEX should work for 3.x and up:
# http://dev.mysql.com/doc/mysql/en/SHOW_INDEX.html
$table = $this->tableName( $table );
+ $index = $this->indexName( $index );
$sql = 'SHOW INDEX FROM '.$table;
$res = $this->query( $sql, $fname );
if ( !$res ) {
@@ -1396,7 +1422,7 @@ class Database {
} else {
$list .= $field." IN (".$this->makeList($value).") ";
}
- } elseif( is_null($value) ) {
+ } elseif( $value === null ) {
if ( $mode == LIST_AND || $mode == LIST_OR ) {
$list .= "$field IS ";
} elseif ( $mode == LIST_SET ) {
@@ -1574,6 +1600,23 @@ class Database {
}
/**
+ * Get the name of an index in a given table
+ */
+ function indexName( $index ) {
+ // Backwards-compatibility hack
+ $renamed = array(
+ 'ar_usertext_timestamp' => 'usertext_timestamp',
+ 'un_user_id' => 'user_id',
+ 'un_user_ip' => 'user_ip',
+ );
+ if( isset( $renamed[$index] ) ) {
+ return $renamed[$index];
+ } else {
+ return $index;
+ }
+ }
+
+ /**
* Wrapper for addslashes()
* @param $s String: to be slashed.
* @return String: slashed string.
@@ -1587,7 +1630,7 @@ class Database {
* Otherwise returns as-is
*/
function addQuotes( $s ) {
- if ( is_null( $s ) ) {
+ if ( $s === null ) {
return 'NULL';
} else {
# This will also quote numeric values. This should be harmless,
@@ -1602,6 +1645,7 @@ class Database {
* Escape string for safe LIKE usage
*/
function escapeLike( $s ) {
+ $s=str_replace('\\','\\\\',$s);
$s=$this->strencode( $s );
$s=str_replace(array('%','_'),array('\%','\_'),$s);
return $s;
@@ -1621,7 +1665,7 @@ class Database {
* PostgreSQL doesn't have them and returns ""
*/
function useIndexClause( $index ) {
- return "FORCE INDEX ($index)";
+ return "FORCE INDEX (" . $this->indexName( $index ) . ")";
}
/**
@@ -1817,6 +1861,14 @@ class Database {
}
/**
+ * Determines if the last query error was something that should be dealt
+ * with by pinging the connection and reissuing the query
+ */
+ function wasErrorReissuable() {
+ return $this->lastErrno() == 2013 || $this->lastErrno() == 2006;
+ }
+
+ /**
* Perform a deadlock-prone transaction.
*
* This function invokes a callback function to perform a set of write
@@ -2250,8 +2302,12 @@ class Database {
}
// Table prefixes
- $ins = preg_replace_callback( '/\/\*(?:\$wgDBprefix|_)\*\/([a-zA-Z_0-9]*)/',
- array( &$this, 'tableNameCallback' ), $ins );
+ $ins = preg_replace_callback( '!/\*(?:\$wgDBprefix|_)\*/([a-zA-Z_0-9]*)!',
+ array( $this, 'tableNameCallback' ), $ins );
+
+ // Index names
+ $ins = preg_replace_callback( '!/\*i\*/([a-zA-Z_0-9]*)!',
+ array( $this, 'indexNameCallback' ), $ins );
return $ins;
}
@@ -2263,6 +2319,13 @@ class Database {
return $this->tableName( $matches[1] );
}
+ /**
+ * Index name callback
+ */
+ protected function indexNameCallback( $matches ) {
+ return $this->indexName( $matches[1] );
+ }
+
/*
* Build a concatenation list to feed into a SQL query
*/
@@ -2480,44 +2543,27 @@ class DBConnectionError extends DBError {
}
function getPageTitle() {
- global $wgSitename;
- return "$wgSitename has a problem";
+ global $wgSitename, $wgLang;
+ $header = "$wgSitename has a problem";
+ if ( $wgLang instanceof Language ) {
+ $header = htmlspecialchars( $wgLang->getMessage( 'dberr-header' ) );
+ }
+
+ return $header;
}
function getHTML() {
- global $wgTitle, $wgUseFileCache, $title, $wgInputEncoding;
- global $wgSitename, $wgServer, $wgMessageCache;
-
- # I give up, Brion is right. Getting the message cache to work when there is no DB is tricky.
- # Hard coding strings instead.
+ global $wgLang, $wgMessageCache, $wgUseFileCache;
- $noconnect = "<p><strong>Sorry! This site is experiencing technical difficulties.</strong></p><p>Try waiting a few minutes and reloading.</p><p><small>(Can't contact the database server: $1)</small></p>";
- $mainpage = 'Main Page';
- $searchdisabled = <<<EOT
-<p style="margin: 1.5em 2em 1em">$wgSitename search is disabled for performance reasons. You can search via Google in the meantime.
-<span style="font-size: 89%; display: block; margin-left: .2em">Note that their indexes of $wgSitename content may be out of date.</span></p>',
-EOT;
+ $sorry = 'Sorry! This site is experiencing technical difficulties.';
+ $again = 'Try waiting a few minutes and reloading.';
+ $info = '(Can\'t contact the database server: $1)';
- $googlesearch = "
-<!-- SiteSearch Google -->
-<FORM method=GET action=\"http://www.google.com/search\">
-<TABLE bgcolor=\"#FFFFFF\"><tr><td>
-<A HREF=\"http://www.google.com/\">
-<IMG SRC=\"http://www.google.com/logos/Logo_40wht.gif\"
-border=\"0\" ALT=\"Google\"></A>
-</td>
-<td>
-<INPUT TYPE=text name=q size=31 maxlength=255 value=\"$1\">
-<INPUT type=submit name=btnG VALUE=\"Google Search\">
-<font size=-1>
-<input type=hidden name=domains value=\"$wgServer\"><br /><input type=radio name=sitesearch value=\"\"> WWW <input type=radio name=sitesearch value=\"$wgServer\" checked> $wgServer <br />
-<input type='hidden' name='ie' value='$2'>
-<input type='hidden' name='oe' value='$2'>
-</font>
-</td></tr></TABLE>
-</FORM>
-<!-- SiteSearch Google -->";
- $cachederror = "The following is a cached copy of the requested page, and may not be up to date. ";
+ if ( $wgLang instanceof Language ) {
+ $sorry = htmlspecialchars( $wgLang->getMessage( 'dberr-problems' ) );
+ $again = htmlspecialchars( $wgLang->getMessage( 'dberr-again' ) );
+ $info = htmlspecialchars( $wgLang->getMessage( 'dberr-info' ) );
+ }
# No database access
if ( is_object( $wgMessageCache ) ) {
@@ -2528,6 +2574,7 @@ border=\"0\" ALT=\"Google\"></A>
$this->error = $this->db->getProperty('mServer');
}
+ $noconnect = "<p><strong>$sorry</strong><br />$again</p><p><small>$info</small></p>";
$text = str_replace( '$1', $this->error, $noconnect );
/*
@@ -2537,38 +2584,95 @@ border=\"0\" ALT=\"Google\"></A>
"</p>\n";
}*/
- if($wgUseFileCache) {
- if($wgTitle) {
- $t =& $wgTitle;
- } else {
- if($title) {
- $t = Title::newFromURL( $title );
- } elseif (@/**/$_REQUEST['search']) {
- $search = $_REQUEST['search'];
- return $searchdisabled .
- str_replace( array( '$1', '$2' ), array( htmlspecialchars( $search ),
- $wgInputEncoding ), $googlesearch );
- } else {
- $t = Title::newFromText( $mainpage );
+ $extra = $this->searchForm();
+
+ if( $wgUseFileCache ) {
+ $cache = $this->fileCachedPage();
+ # Cached version on file system?
+ if( $cache !== null ) {
+ # Hack: extend the body for error messages
+ $cache = str_replace( array('</html>','</body>'), '', $cache );
+ # Add cache notice...
+ $cachederror = "This is a cached copy of the requested page, and may not be up to date. ";
+ # Localize it if possible...
+ if( $wgLang instanceof Language ) {
+ $cachederror = htmlspecialchars( $wgLang->getMessage( 'dberr-cachederror' ) );
}
+ $warning = "<div style='color:red;font-size:150%;font-weight:bold;'>$cachederror</div>";
+ # Output cached page with notices on bottom and re-close body
+ return "{$cache}{$warning}<hr />$text<hr />$extra</body></html>";
}
+ }
+ # Headers needed here - output is just the error message
+ return $this->htmlHeader()."$text<hr />$extra".$this->htmlFooter();
+ }
- $cache = new HTMLFileCache( $t );
- if( $cache->isFileCached() ) {
- // @todo, FIXME: $msg is not defined on the next line.
- $msg = '<p style="color: red"><b>'.$text."<br />\n" .
- $cachederror . "</b></p>\n";
-
- $tag = '<div id="article">';
- $text = str_replace(
- $tag,
- $tag . $text,
- $cache->fetchPageText() );
- }
+ function searchForm() {
+ global $wgSitename, $wgServer, $wgLang, $wgInputEncoding;
+ $usegoogle = "You can try searching via Google in the meantime.";
+ $outofdate = "Note that their indexes of our content may be out of date.";
+ $googlesearch = "Search";
+
+ if ( $wgLang instanceof Language ) {
+ $usegoogle = htmlspecialchars( $wgLang->getMessage( 'dberr-usegoogle' ) );
+ $outofdate = htmlspecialchars( $wgLang->getMessage( 'dberr-outofdate' ) );
+ $googlesearch = htmlspecialchars( $wgLang->getMessage( 'searchbutton' ) );
+ }
+
+ $search = htmlspecialchars(@$_REQUEST['search']);
+
+ $trygoogle = <<<EOT
+<div style="margin: 1.5em">$usegoogle<br />
+<small>$outofdate</small></div>
+<!-- SiteSearch Google -->
+<form method="get" action="http://www.google.com/search" id="googlesearch">
+ <input type="hidden" name="domains" value="$wgServer" />
+ <input type="hidden" name="num" value="50" />
+ <input type="hidden" name="ie" value="$wgInputEncoding" />
+ <input type="hidden" name="oe" value="$wgInputEncoding" />
+
+ <img src="http://www.google.com/logos/Logo_40wht.gif" alt="" style="float:left; margin-left: 1.5em; margin-right: 1.5em;" />
+
+ <input type="text" name="q" size="31" maxlength="255" value="$search" />
+ <input type="submit" name="btnG" value="$googlesearch" />
+ <div>
+ <input type="radio" name="sitesearch" id="gwiki" value="$wgServer" checked="checked" /><label for="gwiki">$wgSitename</label>
+ <input type="radio" name="sitesearch" id="gWWW" value="" /><label for="gWWW">WWW</label>
+ </div>
+</form>
+<!-- SiteSearch Google -->
+EOT;
+ return $trygoogle;
+ }
+
+ function fileCachedPage() {
+ global $wgTitle, $title, $wgLang, $wgOut;
+ if( $wgOut->isDisabled() ) return; // Done already?
+ $mainpage = 'Main Page';
+ if ( $wgLang instanceof Language ) {
+ $mainpage = htmlspecialchars( $wgLang->getMessage( 'mainpage' ) );
}
- return $text;
+ if($wgTitle) {
+ $t =& $wgTitle;
+ } elseif($title) {
+ $t = Title::newFromURL( $title );
+ } else {
+ $t = Title::newFromText( $mainpage );
+ }
+
+ $cache = new HTMLFileCache( $t );
+ if( $cache->isFileCached() ) {
+ return $cache->fetchPageText();
+ } else {
+ return '';
+ }
}
+
+ function htmlBodyOnly() {
+ return true;
+ }
+
}
/**
@@ -2656,7 +2760,7 @@ class ResultWrapper implements Iterator {
* Get the number of rows in a result object
*/
function numRows() {
- return $this->db->numRows( $this->result );
+ return $this->db->numRows( $this );
}
/**
@@ -2669,7 +2773,7 @@ class ResultWrapper implements Iterator {
* @throws DBUnexpectedError Thrown if the database returns an error
*/
function fetchObject() {
- return $this->db->fetchObject( $this->result );
+ return $this->db->fetchObject( $this );
}
/**
@@ -2681,14 +2785,14 @@ class ResultWrapper implements Iterator {
* @throws DBUnexpectedError Thrown if the database returns an error
*/
function fetchRow() {
- return $this->db->fetchRow( $this->result );
+ return $this->db->fetchRow( $this );
}
/**
* Free a result object
*/
function free() {
- $this->db->freeResult( $this->result );
+ $this->db->freeResult( $this );
unset( $this->result );
unset( $this->db );
}
@@ -2698,7 +2802,7 @@ class ResultWrapper implements Iterator {
* See mysql_data_seek()
*/
function seek( $row ) {
- $this->db->dataSeek( $this->result, $row );
+ $this->db->dataSeek( $this, $row );
}
/*********************
@@ -2709,7 +2813,7 @@ class ResultWrapper implements Iterator {
function rewind() {
if ($this->numRows()) {
- $this->db->dataSeek($this->result, 0);
+ $this->db->dataSeek($this, 0);
}
$this->pos = 0;
$this->currentRow = null;