From b9b85843572bf283f48285001e276ba7e61b63f6 Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Sun, 22 Feb 2009 13:37:51 +0100 Subject: updated to MediaWiki 1.14.0 --- includes/db/Database.php | 308 +++++++++++++++++++++++---------------- includes/db/DatabaseMssql.php | 59 ++++---- includes/db/DatabaseOracle.php | 14 +- includes/db/DatabasePostgres.php | 89 ++++++----- includes/db/DatabaseSqlite.php | 43 +++++- includes/db/LBFactory_Multi.php | 14 +- includes/db/LoadBalancer.php | 77 +++++----- includes/db/LoadMonitor.php | 3 + 8 files changed, 360 insertions(+), 247 deletions(-) (limited to 'includes/db') diff --git a/includes/db/Database.php b/includes/db/Database.php index 885ede54..84b88643 100644 --- a/includes/db/Database.php +++ b/includes/db/Database.php @@ -205,12 +205,17 @@ class Database { return false; } - /**#@+ - * Get function + /** + * Return the last query that went through Database::query() + * @return String */ function lastQuery() { return $this->mLastQuery; } + + /** + * Is a connection to the database open? + * @return Boolean + */ function isOpen() { return $this->mOpened; } - /**#@-*/ function setFlag( $flag ) { $this->mFlags |= $flag; @@ -243,13 +248,13 @@ class Database { # Other functions #------------------------------------------------------------------------------ - /**@{{ + /** * Constructor. - * @param string $server database server host - * @param string $user database user name - * @param string $password database user password - * @param string $dbname database name - * @param failFunction + * @param $server String: database server host + * @param $user String: database user name + * @param $password String: database user password + * @param $dbName String: database name + * @param $failFunction * @param $flags * @param $tablePrefix String: database table prefixes. By default use the prefix gave in LocalSettings.php */ @@ -293,7 +298,11 @@ class Database { } /** - * @static + * Same as new Database( ... ), kept for backward compatibility + * @param $server String: database server host + * @param $user String: database user name + * @param $password String: database user password + * @param $dbName String: database name * @param failFunction * @param $flags */ @@ -305,9 +314,13 @@ class Database { /** * Usually aborts on failure * If the failFunction is set to a non-zero integer, returns success + * @param $server String: database server host + * @param $user String: database user name + * @param $password String: database user password + * @param $dbName String: database name */ function open( $server, $user, $password, $dbName ) { - global $wguname, $wgAllDBsAreLocalhost; + global $wgAllDBsAreLocalhost; wfProfileIn( __METHOD__ ); # Test for missing mysql.so @@ -338,13 +351,17 @@ class Database { wfProfileIn("dbconnect-$server"); - # Try to connect up to three times # The kernel's default SYN retransmission period is far too slow for us, - # so we use a short timeout plus a manual retry. + # so we use a short timeout plus a manual retry. Retrying means that a small + # but finite rate of SYN packet loss won't cause user-visible errors. $this->mConn = false; - $max = 3; + if ( ini_get( 'mysql.connect_timeout' ) <= 3 ) { + $numAttempts = 2; + } else { + $numAttempts = 1; + } $this->installErrorHandler(); - for ( $i = 0; $i < $max && !$this->mConn; $i++ ) { + for ( $i = 0; $i < $numAttempts && !$this->mConn; $i++ ) { if ( $i > 1 ) { usleep( 1000 ); } @@ -360,23 +377,28 @@ class Database { } } $phpError = $this->restoreErrorHandler(); + # Always log connection errors + if ( !$this->mConn ) { + $error = $this->lastError(); + if ( !$error ) { + $error = $phpError; + } + wfLogDBError( "Error connecting to {$this->mServer}: $error\n" ); + wfDebug( "DB connection error\n" ); + wfDebug( "Server: $server, User: $user, Password: " . + substr( $password, 0, 3 ) . "..., error: " . mysql_error() . "\n" ); + $success = false; + } wfProfileOut("dbconnect-$server"); - if ( $dbName != '' ) { - if ( $this->mConn !== false ) { - $success = @/**/mysql_select_db( $dbName, $this->mConn ); - if ( !$success ) { - $error = "Error selecting database $dbName on server {$this->mServer} " . - "from client host {$wguname['nodename']}\n"; - wfLogDBError(" Error selecting database $dbName on server {$this->mServer} \n"); - wfDebug( $error ); - } - } else { - wfDebug( "DB connection error\n" ); - wfDebug( "Server: $server, User: $user, Password: " . - substr( $password, 0, 3 ) . "..., error: " . mysql_error() . "\n" ); - $success = false; + if ( $dbName != '' && $this->mConn !== false ) { + $success = @/**/mysql_select_db( $dbName, $this->mConn ); + if ( !$success ) { + $error = "Error selecting database $dbName on server {$this->mServer} " . + "from client host " . wfHostname() . "\n"; + wfLogDBError(" Error selecting database $dbName on server {$this->mServer} \n"); + wfDebug( $error ); } } else { # Delay USE query @@ -405,16 +427,25 @@ class Database { wfProfileOut( __METHOD__ ); return $success; } - /**@}}*/ protected function installErrorHandler() { $this->mPHPError = false; + $this->htmlErrors = ini_set( 'html_errors', '0' ); set_error_handler( array( $this, 'connectionErrorHandler' ) ); } protected function restoreErrorHandler() { restore_error_handler(); - return $this->mPHPError; + if ( $this->htmlErrors !== false ) { + ini_set( 'html_errors', $this->htmlErrors ); + } + if ( $this->mPHPError ) { + $error = preg_replace( '!\[\]!', '', $this->mPHPError ); + $error = preg_replace( '!^.*?:(.*)$!', '$1', $error ); + return $error; + } else { + return false; + } } protected function connectionErrorHandler( $errno, $errstr ) { @@ -425,7 +456,7 @@ class Database { * Closes a database connection. * if it is open : commits any open transactions * - * @return bool operation success. true if already closed. + * @return Bool operation success. true if already closed. */ function close() { @@ -441,7 +472,7 @@ class Database { } /** - * @param string $error fallback error message, used if none is given by MySQL + * @param $error String: fallback error message, used if none is given by MySQL */ function reportConnectionError( $error = 'Unknown error' ) { $myError = $this->lastError(); @@ -457,7 +488,6 @@ class Database { } } else { # New method - wfLogDBError( "Connection error: $error\n" ); throw new DBConnectionError( $this, $error ); } } @@ -468,7 +498,7 @@ class Database { * @param $sql String: SQL query * @param $fname String: Name of the calling function, for profiling/SHOW PROCESSLIST * comment (you can use __METHOD__ or add some extra info) - * @param $tempIgnore Bool: Whether to avoid throwing an exception on errors... + * @param $tempIgnore Boolean: Whether to avoid throwing an exception on errors... * maybe best to catch the exception instead? * @return true for a successful write query, ResultWrapper object for a successful read query, * or false on failure if $tempIgnore set @@ -572,7 +602,7 @@ class Database { * The DBMS-dependent part of query() * @param $sql String: SQL query. * @return Result object to feed to fetchObject, fetchRow, ...; or false on failure - * @access private + * @private */ /*private*/ function doQuery( $sql ) { if( $this->bufferResults() ) { @@ -584,11 +614,11 @@ class Database { } /** - * @param $error - * @param $errno - * @param $sql - * @param string $fname - * @param bool $tempIgnore + * @param $error String + * @param $errno Integer + * @param $sql String + * @param $fname String + * @param $tempIgnore Boolean */ function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) { global $wgCommandLineMode; @@ -630,8 +660,8 @@ class Database { /** * Execute a prepared query with the various arguments - * @param string $prepared the prepared sql - * @param mixed $args Either an array here, or put scalars as varargs + * @param $prepared String: the prepared sql + * @param $args Mixed: Either an array here, or put scalars as varargs */ function execute( $prepared, $args = null ) { if( !is_array( $args ) ) { @@ -646,8 +676,8 @@ class Database { /** * Prepare & execute an SQL statement, quoting and inserting arguments * in the appropriate places. - * @param string $query - * @param string $args ... + * @param $query String + * @param $args ... */ function safeQuery( $query, $args = null ) { $prepared = $this->prepare( $query, 'Database::safeQuery' ); @@ -664,8 +694,8 @@ class Database { /** * For faking prepared SQL statements on DBs that don't support * it directly. - * @param string $preparedSql - a 'preparable' SQL statement - * @param array $args - array of arguments to fill it with + * @param $preparedQuery String: a 'preparable' SQL statement + * @param $args Array of arguments to fill it with * @return string executable SQL */ function fillPrepared( $preparedQuery, $args ) { @@ -680,8 +710,8 @@ class Database { * The arguments should be in $this->preparedArgs and must not be touched * while we're doing this. * - * @param array $matches - * @return string + * @param $matches Array + * @return String * @private */ function fillPreparedArg( $matches ) { @@ -702,11 +732,9 @@ class Database { } } - /**#@+ - * @param mixed $res A SQL result - */ /** * Free a result object + * @param $res Mixed: A SQL result */ function freeResult( $res ) { if ( $res instanceof ResultWrapper ) { @@ -758,6 +786,7 @@ class Database { /** * Get the number of rows in a result object + * @param $res Mixed: A SQL result */ function numRows( $res ) { if ( $res instanceof ResultWrapper ) { @@ -773,6 +802,7 @@ class Database { /** * Get the number of fields in a result object * See documentation for mysql_num_fields() + * @param $res Mixed: A SQL result */ function numFields( $res ) { if ( $res instanceof ResultWrapper ) { @@ -785,6 +815,8 @@ class Database { * Get a field name in a result object * See documentation for mysql_field_name(): * http://www.php.net/mysql_field_name + * @param $res Mixed: A SQL result + * @param $n Integer */ function fieldName( $res, $n ) { if ( $res instanceof ResultWrapper ) { @@ -808,6 +840,8 @@ class Database { /** * Change the position of the cursor in a result object * See mysql_data_seek() + * @param $res Mixed: A SQL result + * @param $row Mixed: Either MySQL row or ResultWrapper */ function dataSeek( $res, $row ) { if ( $res instanceof ResultWrapper ) { @@ -854,7 +888,6 @@ class Database { * See mysql_affected_rows() for more details */ function affectedRows() { return mysql_affected_rows( $this->mConn ); } - /**#@-*/ // end of template : @param $result /** * Simple UPDATE wrapper @@ -864,8 +897,7 @@ class Database { * This function exists for historical reasons, Database::update() has a more standard * calling convention and feature set */ - function set( $table, $var, $value, $cond, $fname = 'Database::set' ) - { + function set( $table, $var, $value, $cond, $fname = 'Database::set' ) { $table = $this->tableName( $table ); $sql = "UPDATE $table SET $var = '" . $this->strencode( $value ) . "' WHERE ($cond)"; @@ -890,7 +922,7 @@ class Database { $row = $this->fetchRow( $res ); if ( $row !== false ) { $this->freeResult( $res ); - return $row[0]; + return reset( $row ); } else { return false; } @@ -902,9 +934,9 @@ class Database { * * @private * - * @param array $options an associative array of options to be turned into + * @param $options Array: associative array of options to be turned into * an SQL query, valid keys are listed in the function. - * @return array + * @return Array */ function makeSelectOptions( $options ) { $preLimitTail = $postLimitTail = ''; @@ -953,14 +985,14 @@ class Database { /** * SELECT wrapper * - * @param mixed $table Array or string, table name(s) (prefix auto-added) - * @param mixed $vars Array or string, field name(s) to be retrieved - * @param mixed $conds Array or string, condition(s) for WHERE - * @param string $fname Calling function name (use __METHOD__) for logs/profiling - * @param array $options Associative array of options (e.g. array('GROUP BY' => 'page_title')), - * see Database::makeSelectOptions code for list of supported stuff - * @param array $join_conds Associative array of table join conditions (optional) - * (e.g. array( 'page' => array('LEFT JOIN','page_latest=rev_id') ) + * @param $table Mixed: Array or string, table name(s) (prefix auto-added) + * @param $vars Mixed: Array or string, field name(s) to be retrieved + * @param $conds Mixed: Array or string, condition(s) for WHERE + * @param $fname String: Calling function name (use __METHOD__) for logs/profiling + * @param $options Array: Associative array of options (e.g. array('GROUP BY' => 'page_title')), + * see Database::makeSelectOptions code for list of supported stuff + * @param $join_conds Array: Associative array of table join conditions (optional) + * (e.g. array( 'page' => array('LEFT JOIN','page_latest=rev_id') ) * @return mixed Database result resource (feed to Database::fetchObject or whatever), or false on failure */ function select( $table, $vars, $conds='', $fname = 'Database::select', $options = array(), $join_conds = array() ) @@ -972,14 +1004,14 @@ class Database { /** * SELECT wrapper * - * @param mixed $table Array or string, table name(s) (prefix auto-added) - * @param mixed $vars Array or string, field name(s) to be retrieved - * @param mixed $conds Array or string, condition(s) for WHERE - * @param string $fname Calling function name (use __METHOD__) for logs/profiling - * @param array $options Associative array of options (e.g. array('GROUP BY' => 'page_title')), - * see Database::makeSelectOptions code for list of supported stuff - * @param array $join_conds Associative array of table join conditions (optional) - * (e.g. array( 'page' => array('LEFT JOIN','page_latest=rev_id') ) + * @param $table Mixed: Array or string, table name(s) (prefix auto-added) + * @param $vars Mixed: Array or string, field name(s) to be retrieved + * @param $conds Mixed: Array or string, condition(s) for WHERE + * @param $fname String: Calling function name (use __METHOD__) for logs/profiling + * @param $options Array: Associative array of options (e.g. array('GROUP BY' => 'page_title')), + * see Database::makeSelectOptions code for list of supported stuff + * @param $join_conds Array: Associative array of table join conditions (optional) + * (e.g. array( 'page' => array('LEFT JOIN','page_latest=rev_id') ) * @return string, the SQL text */ function selectSQLText( $table, $vars, $conds='', $fname = 'Database::select', $options = array(), $join_conds = array() ) { @@ -1030,13 +1062,17 @@ class Database { * Single row SELECT wrapper * Aborts or returns FALSE on error * - * $vars: the selected variables - * $conds: a condition map, terms are ANDed together. + * @param $table String: table name + * @param $vars String: the selected variables + * @param $conds Array: 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: selectRow( "page", array( "page_id" ), array( "page_namespace" => * NS_MAIN, "page_title" => "Astronomy" ) ) would return an object where * $obj- >page_id is the ID of the Astronomy article + * @param $fname String: Calling functio name + * @param $options Array + * @param $join_conds Array * * @todo migrate documentation to phpdocumentor format */ @@ -1086,8 +1122,7 @@ class Database { * 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. * - * @param string $sql A SQL Query - * @static + * @param $sql String: A SQL Query */ static function generalizeSQL( $sql ) { # This does the same as the regexp below would do, but in such a way @@ -1280,7 +1315,7 @@ class Database { * Make UPDATE options for the Database::update function * * @private - * @param array $options The options passed to Database::update + * @param $options Array: The options passed to Database::update * @return string */ function makeUpdateOptions( $options ) { @@ -1298,14 +1333,14 @@ class Database { /** * UPDATE wrapper, takes a condition array and a SET array * - * @param string $table The table to UPDATE - * @param array $values An array of values to SET - * @param array $conds An array of conditions (WHERE). Use '*' to update all rows. - * @param string $fname The Class::Function calling this function - * (for the log) - * @param array $options An array of UPDATE options, can be one or + * @param $table String: The table to UPDATE + * @param $values Array: An array of values to SET + * @param $conds Array: An array of conditions (WHERE). Use '*' to update all rows. + * @param $fname String: The Class::Function calling this function + * (for the log) + * @param $options Array: An array of UPDATE options, can be one or * more of IGNORE, LOW_PRIORITY - * @return bool + * @return Boolean */ function update( $table, $values, $conds, $fname = 'Database::update', $options = array() ) { $table = $this->tableName( $table ); @@ -1410,8 +1445,8 @@ class Database { * themselves. Pass the canonical name to such functions. This is only needed * when calling query() directly. * - * @param string $name database table name - * @return string full database name + * @param $name String: database table name + * @return String: full database name */ function tableName( $name ) { global $wgSharedDB, $wgSharedPrefix, $wgSharedTables; @@ -1540,8 +1575,8 @@ class Database { /** * Wrapper for addslashes() - * @param string $s String to be slashed. - * @return string slashed string. + * @param $s String: to be slashed. + * @return String: slashed string. */ function strencode( $s ) { return mysql_real_escape_string( $s, $this->mConn ); @@ -1632,11 +1667,12 @@ class Database { * * DO NOT put the join condition in $conds * - * @param string $delTable The table to delete from. - * @param string $joinTable The other table. - * @param string $delVar The variable to join on, in the first table. - * @param string $joinVar The variable to join on, in the second table. - * @param array $conds Condition array of field names mapped to variables, ANDed together in the WHERE clause + * @param $delTable String: The table to delete from. + * @param $joinTable String: The other table. + * @param $delVar String: The variable to join on, in the first table. + * @param $joinVar String: The variable to join on, in the second table. + * @param $conds Array: Condition array of field names mapped to variables, ANDed together in the WHERE clause + * @param $fname String: Calling function name (use __METHOD__) for logs/profiling */ function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = 'Database::deleteJoin' ) { if ( !$conds ) { @@ -1732,9 +1768,9 @@ class Database { /** * Construct a LIMIT query with optional offset * This is used for query pages - * $sql string SQL query we will append the limit too - * $limit integer the SQL limit - * $offset integer the SQL offset (default false) + * @param $sql String: SQL query we will append the limit too + * @param $limit Integer: the SQL limit + * @param $offset Integer the SQL offset (default false) */ function limitResult($sql, $limit, $offset=false) { if( !is_numeric($limit) ) { @@ -1752,10 +1788,10 @@ class Database { * Returns an SQL expression for a simple conditional. * Uses IF on MySQL. * - * @param string $cond SQL expression which will result in a boolean value - * @param string $trueVal SQL expression to return if true - * @param string $falseVal SQL expression to return if false - * @return string SQL fragment + * @param $cond String: SQL expression which will result in a boolean value + * @param $trueVal String: SQL expression to return if true + * @param $falseVal String: SQL expression to return if false + * @return String: SQL fragment */ function conditional( $cond, $trueVal, $falseVal ) { return " IF($cond, $trueVal, $falseVal) "; @@ -1765,9 +1801,9 @@ class Database { * Returns a comand for str_replace function in SQL query. * Uses REPLACE() in MySQL * - * @param string $orig String or column to modify - * @param string $old String or column to seek - * @param string $new String or column to replace with + * @param $orig String: column to modify + * @param $old String: column to seek + * @param $new String: column to replace with */ function strreplace( $orig, $old, $new ) { return "REPLACE({$orig}, {$old}, {$new})"; @@ -1838,9 +1874,8 @@ class Database { /** * Do a SELECT MASTER_POS_WAIT() * - * @param string $file the binlog file - * @param string $pos the binlog position - * @param integer $timeout the maximum number of seconds to wait for synchronisation + * @param $pos MySQLMasterPos object + * @param $timeout Integer: the maximum number of seconds to wait for synchronisation */ function masterPosWait( MySQLMasterPos $pos, $timeout ) { $fname = 'Database::masterPosWait'; @@ -1896,7 +1931,8 @@ class Database { $res = $this->query( 'SHOW SLAVE STATUS', 'Database::getSlavePos' ); $row = $this->fetchObject( $res ); if ( $row ) { - return new MySQLMasterPos( $row->Master_Log_File, $row->Read_Master_Log_Pos ); + $pos = isset($row->Exec_master_log_pos) ? $row->Exec_master_log_pos : $row->Exec_Master_Log_Pos; + return new MySQLMasterPos( $row->Relay_Master_Log_File, $pos ); } else { return false; } @@ -2001,14 +2037,14 @@ class Database { } /** - * @return string wikitext of a link to the server software's web site + * @return String: wikitext of a link to the server software's web site */ function getSoftwareLink() { return "[http://www.mysql.com/ MySQL]"; } /** - * @return string Version information from the database + * @return String: Version information from the database */ function getServerVersion() { return mysql_get_server_info( $this->mConn ); @@ -2106,7 +2142,7 @@ class Database { * May be useful for very long batch queries such as * full-wiki dumps, where a single query reads out * over hours or days. - * @param int $timeout in seconds + * @param $timeout Integer in seconds */ public function setTimeout( $timeout ) { $this->query( "SET net_read_timeout=$timeout" ); @@ -2116,9 +2152,9 @@ class Database { /** * Read and execute SQL commands from a file. * Returns true on success, error string or exception on failure (depending on object's error ignore settings) - * @param string $filename File name to open - * @param callback $lineCallback Optional function called before reading each line - * @param callback $resultCallback Optional function called for each MySQL result + * @param $filename String: File name to open + * @param $lineCallback Callback: Optional function called before reading each line + * @param $resultCallback Callback: Optional function called for each MySQL result */ function sourceFile( $filename, $lineCallback = false, $resultCallback = false ) { $fp = fopen( $filename, 'r' ); @@ -2133,9 +2169,9 @@ class Database { /** * Read and execute commands from an open file handle * Returns true on success, error string or exception on failure (depending on object's error ignore settings) - * @param string $fp File handle - * @param callback $lineCallback Optional function called before reading each line - * @param callback $resultCallback Optional function called for each MySQL result + * @param $fp String: File handle + * @param $lineCallback Callback: Optional function called before reading each line + * @param $resultCallback Callback: Optional function called for each MySQL result */ function sourceStream( $fp, $lineCallback = false, $resultCallback = false ) { $cmd = ""; @@ -2177,7 +2213,7 @@ class Database { $cmd = $this->replaceVars( $cmd ); $res = $this->query( $cmd, __METHOD__ ); if ( $resultCallback ) { - call_user_func( $resultCallback, $res ); + call_user_func( $resultCallback, $res, $this ); } if ( false === $res ) { @@ -2240,8 +2276,8 @@ class Database { * Abstracted from Filestore::lock() so child classes can implement for * their own needs. * - * @param string $lockName Name of lock to aquire - * @param string $method Name of method calling us + * @param $lockName String: Name of lock to aquire + * @param $method String: Name of method calling us * @return bool */ public function lock( $lockName, $method ) { @@ -2263,14 +2299,24 @@ class Database { * @todo fixme - Figure out a way to return a bool * based on successful lock release. * - * @param string $lockName Name of lock to release - * @param string $method Name of method calling us + * @param $lockName String: Name of lock to release + * @param $method String: Name of method calling us */ public function unlock( $lockName, $method ) { $lockName = $this->addQuotes( $lockName ); $result = $this->query( "SELECT RELEASE_LOCK($lockName)", $method ); $this->freeResult( $result ); } + + /** + * Get search engine class. All subclasses of this + * need to implement this if they wish to use searching. + * + * @return String + */ + public function getSearchEngine() { + return "SearchMySQL"; + } } /** @@ -2390,8 +2436,8 @@ class DBError extends MWException { /** * Construct a database error - * @param Database $db The database object which threw the error - * @param string $error A simple error message to be used for debugging + * @param $db Database object which threw the error + * @param $error A simple error message to be used for debugging */ function __construct( Database &$db, $error ) { $this->db =& $db; @@ -2483,7 +2529,13 @@ border=\"0\" ALT=\"Google\"> } $text = str_replace( '$1', $this->error, $noconnect ); - $text .= wfGetSiteNotice(); + + /* + if ( $GLOBALS['wgShowExceptionDetails'] ) { + $text .= '

Backtrace:

' . + nl2br( htmlspecialchars( $this->getTraceAsString() ) ) . + "

\n"; + }*/ if($wgUseFileCache) { if($wgTitle) { @@ -2504,13 +2556,13 @@ border=\"0\" ALT=\"Google\"> $cache = new HTMLFileCache( $t ); if( $cache->isFileCached() ) { // @todo, FIXME: $msg is not defined on the next line. - $msg = '

'.$msg."
\n" . + $msg = '

'.$text."
\n" . $cachederror . "

\n"; $tag = '
'; $text = str_replace( $tag, - $tag . $msg, + $tag . $text, $cache->fetchPageText() ); } } diff --git a/includes/db/DatabaseMssql.php b/includes/db/DatabaseMssql.php index 32fe28b1..28ccab2d 100644 --- a/includes/db/DatabaseMssql.php +++ b/includes/db/DatabaseMssql.php @@ -105,7 +105,7 @@ class DatabaseMssql extends Database { $success = @/**/mssql_select_db($dbName, $this->mConn); if (!$success) { $error = "Error selecting database $dbName on server {$this->mServer} " . - "from client host {$wguname['nodename']}\n"; + "from client host " . wfHostname() . "\n"; wfLogDBError(" Error selecting database $dbName on server {$this->mServer} \n"); wfDebug( $error ); } @@ -154,9 +154,6 @@ class DatabaseMssql extends Database { return $ret; } - /**#@+ - * @param mixed $res A SQL result - */ /** * Free a result object */ @@ -225,6 +222,7 @@ class DatabaseMssql extends Database { /** * Get the number of fields in a result object * See documentation for mysql_num_fields() + * @param $res SQL result object as returned from Database::query(), etc. */ function numFields( $res ) { if ( $res instanceof ResultWrapper ) { @@ -237,6 +235,8 @@ class DatabaseMssql extends Database { * Get a field name in a result object * See documentation for mysql_field_name(): * http://www.php.net/mysql_field_name + * @param $res SQL result object as returned from Database::query(), etc. + * @param $n Int */ function fieldName( $res, $n ) { if ( $res instanceof ResultWrapper ) { @@ -263,6 +263,8 @@ class DatabaseMssql extends Database { /** * Change the position of the cursor in a result object * See mysql_data_seek() + * @param $res SQL result object as returned from Database::query(), etc. + * @param $row Database row */ function dataSeek( $res, $row ) { if ( $res instanceof ResultWrapper ) { @@ -339,7 +341,7 @@ class DatabaseMssql extends Database { * * @private * - * @param array $options an associative array of options to be turned into + * @param $options Array: an associative array of options to be turned into * an SQL query, valid keys are listed in the function. * @return array */ @@ -390,11 +392,11 @@ class DatabaseMssql extends Database { /** * SELECT wrapper * - * @param mixed $table Array or string, table name(s) (prefix auto-added) - * @param mixed $vars Array or string, field name(s) to be retrieved - * @param mixed $conds Array or string, condition(s) for WHERE - * @param string $fname Calling function name (use __METHOD__) for logs/profiling - * @param array $options Associative array of options (e.g. array('GROUP BY' => 'page_title')), + * @param $table Mixed: Array or string, table name(s) (prefix auto-added) + * @param $vars Mixed: Array or string, field name(s) to be retrieved + * @param $conds Mixed: Array or string, condition(s) for WHERE + * @param $fname String: Calling function name (use __METHOD__) for logs/profiling + * @param $options Array: Associative array of options (e.g. array('GROUP BY' => 'page_title')), * see Database::makeSelectOptions code for list of supported stuff * @return mixed Database result resource (feed to Database::fetchObject or whatever), or false on failure */ @@ -643,12 +645,12 @@ class DatabaseMssql extends Database { /** * UPDATE wrapper, takes a condition array and a SET array * - * @param string $table The table to UPDATE - * @param array $values An array of values to SET - * @param array $conds An array of conditions (WHERE). Use '*' to update all rows. - * @param string $fname The Class::Function calling this function - * (for the log) - * @param array $options An array of UPDATE options, can be one or + * @param $table String: The table to UPDATE + * @param $values Array: An array of values to SET + * @param $conds Array: An array of conditions (WHERE). Use '*' to update all rows. + * @param $fname String: The Class::Function calling this function + * (for the log) + * @param $options Array: An array of UPDATE options, can be one or * more of IGNORE, LOW_PRIORITY * @return bool */ @@ -666,7 +668,7 @@ class DatabaseMssql extends Database { * Make UPDATE options for the Database::update function * * @private - * @param array $options The options passed to Database::update + * @param $options Array: The options passed to Database::update * @return string */ function makeUpdateOptions( $options ) { @@ -698,7 +700,7 @@ class DatabaseMssql extends Database { /** * MSSQL doubles quotes instead of escaping them - * @param string $s String to be slashed. + * @param $s String to be slashed. * @return string slashed string. */ function strencode($s) { @@ -755,11 +757,12 @@ class DatabaseMssql extends Database { * * DO NOT put the join condition in $conds * - * @param string $delTable The table to delete from. - * @param string $joinTable The other table. - * @param string $delVar The variable to join on, in the first table. - * @param string $joinVar The variable to join on, in the second table. - * @param array $conds Condition array of field names mapped to variables, ANDed together in the WHERE clause + * @param $delTable String: The table to delete from. + * @param $joinTable String: The other table. + * @param $delVar String: The variable to join on, in the first table. + * @param $joinVar String: The variable to join on, in the second table. + * @param $conds Array: Condition array of field names mapped to variables, ANDed together in the WHERE clause + * @param $fname String: Calling function name */ function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = 'Database::deleteJoin' ) { if ( !$conds ) { @@ -857,9 +860,9 @@ class DatabaseMssql extends Database { /** * Returns an SQL expression for a simple conditional. * - * @param string $cond SQL expression which will result in a boolean value - * @param string $trueVal SQL expression to return if true - * @param string $falseVal SQL expression to return if false + * @param $cond String: SQL expression which will result in a boolean value + * @param $trueVal String: SQL expression to return if true + * @param $falseVal String: SQL expression to return if false * @return string SQL fragment */ function conditional( $cond, $trueVal, $falseVal ) { @@ -1007,6 +1010,10 @@ class DatabaseMssql extends Database { public function unlock( $lockName, $method ) { return true; } + + public function getSearchEngine() { + return "SearchEngineDummy"; + } } diff --git a/includes/db/DatabaseOracle.php b/includes/db/DatabaseOracle.php index f4dbac71..4c37a507 100644 --- a/includes/db/DatabaseOracle.php +++ b/includes/db/DatabaseOracle.php @@ -509,10 +509,10 @@ class DatabaseOracle extends Database { * Returns an SQL expression for a simple conditional. * Uses CASE on Oracle * - * @param string $cond SQL expression which will result in a boolean value - * @param string $trueVal SQL expression to return if true - * @param string $falseVal SQL expression to return if false - * @return string SQL fragment + * @param $cond String: SQL expression which will result in a boolean value + * @param $trueVal String: SQL expression to return if true + * @param $falseVal String: SQL expression to return if false + * @return String: SQL fragment */ function conditional( $cond, $trueVal, $falseVal ) { return " (CASE WHEN $cond THEN $trueVal ELSE $falseVal END) "; @@ -640,7 +640,7 @@ echo "error!\n"; * * @private * - * @param array $options an associative array of options to be turned into + * @param $options Array: an associative array of options to be turned into * an SQL query, valid keys are listed in the function. * @return array */ @@ -716,5 +716,9 @@ echo "error!\n"; public function unlock( $lockName, $method ) { return true; } + + public function getSearchEngine() { + return "SearchOracle"; + } } // end DatabaseOracle class diff --git a/includes/db/DatabasePostgres.php b/includes/db/DatabasePostgres.php index 8fd04cb6..16a74b53 100644 --- a/includes/db/DatabasePostgres.php +++ b/includes/db/DatabasePostgres.php @@ -78,12 +78,6 @@ class DatabasePostgres extends Database { $failFunction = false, $flags = 0 ) { - global $wgOut; - # Can't get a reference if it hasn't been set yet - if ( !isset( $wgOut ) ) { - $wgOut = NULL; - } - $this->mOut =& $wgOut; $this->mFailFunction = $failFunction; $this->mFlags = $flags; $this->open( $server, $user, $password, $dbName); @@ -138,10 +132,9 @@ class DatabasePostgres extends Database { global $wgDBport; - if (!strlen($user)) { ## e.g. the class is being loaded - return; + if (!strlen($user)) { ## e.g. the class is being loaded + return; } - $this->close(); $this->mServer = $server; $this->mPort = $port = $wgDBport; @@ -149,22 +142,31 @@ class DatabasePostgres extends Database { $this->mPassword = $password; $this->mDBname = $dbName; - $hstring=""; + $connectVars = array( + 'dbname' => $dbName, + 'user' => $user, + 'password' => $password ); if ($server!=false && $server!="") { - $hstring="host=$server "; + $connectVars['host'] = $server; } if ($port!=false && $port!="") { - $hstring .= "port=$port "; + $connectVars['port'] = $port; } + $connectString = $this->makeConnectionString( $connectVars ); - error_reporting( E_ALL ); - @$this->mConn = pg_connect("$hstring dbname=$dbName user=$user password=$password"); + $this->installErrorHandler(); + $this->mConn = pg_connect( $connectString ); + $phpError = $this->restoreErrorHandler(); if ( $this->mConn == false ) { wfDebug( "DB connection error\n" ); wfDebug( "Server: $server, Database: $dbName, User: $user, Password: " . substr( $password, 0, 3 ) . "...\n" ); wfDebug( $this->lastError()."\n" ); - return false; + if ( !$this->mFailFunction ) { + throw new DBConnectionError( $this, $phpError ); + } else { + return false; + } } $this->mOpened = true; @@ -189,6 +191,14 @@ class DatabasePostgres extends Database { return $this->mConn; } + function makeConnectionString( $vars ) { + $s = ''; + foreach ( $vars as $name => $value ) { + $s .= "$name='" . str_replace( "'", "\\'", $value ) . "' "; + } + return $s; + } + function initial_setup($password, $dbName) { // If this is the initial connection, setup the schema stuff and possibly create the user @@ -197,9 +207,8 @@ class DatabasePostgres extends Database { print "
  • Checking the version of Postgres..."; $version = $this->getServerVersion(); $PGMINVER = '8.1'; - if ($this->numeric_version < $PGMINVER) { - print "FAILED. Required version is $PGMINVER. You have " . - htmlspecialchars( $this->numeric_version ) . " (" . htmlspecialchars( $version ) . ")
  • \n"; + if ($version < $PGMINVER) { + print "FAILED. Required version is $PGMINVER. You have " . htmlspecialchars( $version ) . "\n"; dieout(""); } print "version " . htmlspecialchars( $this->numeric_version ) . " is OK.\n"; @@ -730,10 +739,10 @@ class DatabasePostgres extends Database { * $args may be a single associative array, or an array of these with numeric keys, * for multi-row insert (Postgres version 8.2 and above only). * - * @param array $table String: Name of the table to insert to. - * @param array $args Array: Items to insert into the table. - * @param array $fname String: Name of the function, for profiling - * @param mixed $options String or Array. Valid options: IGNORE + * @param $table String: Name of the table to insert to. + * @param $args Array: Items to insert into the table. + * @param $fname String: Name of the function, for profiling + * @param $options String or Array. Valid options: IGNORE * * @return bool Success of insert operation. IGNORE always returns true. */ @@ -746,8 +755,7 @@ class DatabasePostgres extends Database { $table = $this->tableName( $table ); if (! isset( $wgDBversion ) ) { - $this->getServerVersion(); - $wgDBversion = $this->numeric_version; + $wgDBversion = $this->getServerVersion(); } if ( !is_array( $options ) ) @@ -1009,10 +1017,10 @@ class DatabasePostgres extends Database { * Returns an SQL expression for a simple conditional. * Uses CASE on Postgres * - * @param string $cond SQL expression which will result in a boolean value - * @param string $trueVal SQL expression to return if true - * @param string $falseVal SQL expression to return if false - * @return string SQL fragment + * @param $cond String: SQL expression which will result in a boolean value + * @param $trueVal String: SQL expression to return if true + * @param $falseVal String: SQL expression to return if false + * @return String: SQL fragment */ function conditional( $cond, $trueVal, $falseVal ) { return " (CASE WHEN $cond THEN $trueVal ELSE $falseVal END) "; @@ -1055,7 +1063,7 @@ class DatabasePostgres extends Database { /** * @return string wikitext of a link to the server software's web site */ - function getSoftwareLink() { + function getSoftwareLink() { return "[http://www.postgresql.org/ PostgreSQL]"; } @@ -1063,16 +1071,17 @@ class DatabasePostgres extends Database { * @return string Version information from the database */ function getServerVersion() { - $version = pg_fetch_result($this->doQuery("SELECT version()"),0,0); - $thisver = array(); - if (!preg_match('/PostgreSQL (\d+\.\d+)(\S+)/', $version, $thisver)) { - die("Could not determine the numeric version from $version!"); + $versionInfo = pg_version( $this->mConn ); + if ( isset( $versionInfo['server'] ) ) { + $this->numeric_version = $versionInfo['server']; + } else { + // There's no way to identify the precise version before 7.4, but + // it doesn't matter anyway since we're just going to give an error. + $this->numeric_version = '7.3 or earlier'; } - $this->numeric_version = $thisver[1]; - return $version; + return $this->numeric_version; } - /** * Query whether a given relation exists (in the given schema, or the * default mw one if not given) @@ -1319,7 +1328,7 @@ END; * * @private * - * @param string $com SQL string, read from a stream (usually tables.sql) + * @param $ins String: SQL string, read from a stream (usually tables.sql) * * @return string SQL string */ @@ -1344,7 +1353,7 @@ END; * * @private * - * @param array $options an associative array of options to be turned into + * @param $options Array: an associative array of options to be turned into * an SQL query, valid keys are listed in the function. * @return array */ @@ -1417,5 +1426,9 @@ END; public function unlock( $lockName, $method ) { return true; } + + public function getSearchEngine() { + return "SearchPostgres"; + } } // end DatabasePostgres class diff --git a/includes/db/DatabaseSqlite.php b/includes/db/DatabaseSqlite.php index 112c417b..dfc506cc 100644 --- a/includes/db/DatabaseSqlite.php +++ b/includes/db/DatabaseSqlite.php @@ -20,11 +20,9 @@ class DatabaseSqlite extends Database { * Constructor */ function __construct($server = false, $user = false, $password = false, $dbName = false, $failFunction = false, $flags = 0) { - global $wgOut,$wgSQLiteDataDir; + global $wgOut,$wgSQLiteDataDir, $wgSQLiteDataDirMode; if ("$wgSQLiteDataDir" == '') $wgSQLiteDataDir = dirname($_SERVER['DOCUMENT_ROOT']).'/data'; - if (!is_dir($wgSQLiteDataDir)) mkdir($wgSQLiteDataDir,0700); - if (!isset($wgOut)) $wgOut = NULL; # Can't get a reference if it hasn't been set yet - $this->mOut =& $wgOut; + if (!is_dir($wgSQLiteDataDir)) wfMkdirParents( $wgSQLiteDataDir, $wgSQLiteDataDirMode ); $this->mFailFunction = $failFunction; $this->mFlags = $flags; $this->mDatabaseFile = "$wgSQLiteDataDir/$dbName.sqlite"; @@ -48,11 +46,28 @@ class DatabaseSqlite extends Database { $this->mConn = false; if ($dbName) { $file = $this->mDatabaseFile; - if ($this->mFlags & DBO_PERSISTENT) $this->mConn = new PDO("sqlite:$file",$user,$pass,array(PDO::ATTR_PERSISTENT => true)); - else $this->mConn = new PDO("sqlite:$file",$user,$pass); - if ($this->mConn === false) wfDebug("DB connection error: $err\n");; + try { + if ( $this->mFlags & DBO_PERSISTENT ) { + $this->mConn = new PDO( "sqlite:$file", $user, $pass, + array( PDO::ATTR_PERSISTENT => true ) ); + } else { + $this->mConn = new PDO( "sqlite:$file", $user, $pass ); + } + } catch ( PDOException $e ) { + $err = $e->getMessage(); + } + if ( $this->mConn === false ) { + wfDebug( "DB connection error: $err\n" ); + if ( !$this->mFailFunction ) { + throw new DBConnectionError( $this, $err ); + } else { + return false; + } + + } $this->mOpened = $this->mConn; - $this->mConn->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_SILENT); # set error codes only, dont raise exceptions + # set error codes only, don't raise exceptions + $this->mConn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT ); } return $this->mConn; } @@ -390,7 +405,19 @@ class DatabaseSqlite extends Database { public function unlock( $lockName, $method ) { return true; } + + public function getSearchEngine() { + return "SearchEngineDummy"; + } + /** + * No-op version of deadlockLoop + */ + public function deadlockLoop( /*...*/ ) { + $args = func_get_args(); + $function = array_shift( $args ); + return call_user_func_array( $function, $args ); + } } /** diff --git a/includes/db/LBFactory_Multi.php b/includes/db/LBFactory_Multi.php index 48c2d99b..820aa2ea 100644 --- a/includes/db/LBFactory_Multi.php +++ b/includes/db/LBFactory_Multi.php @@ -36,6 +36,8 @@ * * masterTemplateOverrides An override array for all master servers. * + * readOnlyBySection A map of section name to read-only message. Missing or false for read/write. + * * @ingroup Database */ class LBFactory_Multi extends LBFactory { @@ -44,7 +46,7 @@ class LBFactory_Multi extends LBFactory { // Optional settings var $groupLoadsBySection = array(), $groupLoadsByDB = array(), $hostsByName = array(); var $externalLoads = array(), $externalTemplateOverrides, $templateOverridesByServer; - var $templateOverridesByCluster, $masterTemplateOverrides; + var $templateOverridesByCluster, $masterTemplateOverrides, $readOnlyBySection = array(); // Other stuff var $conf, $mainLBs = array(), $extLBs = array(); var $lastWiki, $lastSection; @@ -55,7 +57,8 @@ class LBFactory_Multi extends LBFactory { $required = array( 'sectionsByDB', 'sectionLoads', 'serverTemplate' ); $optional = array( 'groupLoadsBySection', 'groupLoadsByDB', 'hostsByName', 'externalLoads', 'externalTemplateOverrides', 'templateOverridesByServer', - 'templateOverridesByCluster', 'masterTemplateOverrides' ); + 'templateOverridesByCluster', 'masterTemplateOverrides', + 'readOnlyBySection' ); foreach ( $required as $key ) { if ( !isset( $conf[$key] ) ) { @@ -69,6 +72,13 @@ class LBFactory_Multi extends LBFactory { $this->$key = $conf[$key]; } } + + // Check for read-only mode + $section = $this->getSectionForWiki(); + if ( !empty( $this->readOnlyBySection[$section] ) ) { + global $wgReadOnly; + $wgReadOnly = $this->readOnlyBySection[$section]; + } } function getSectionForWiki( $wiki = false ) { diff --git a/includes/db/LoadBalancer.php b/includes/db/LoadBalancer.php index 42c4044d..f847fe22 100644 --- a/includes/db/LoadBalancer.php +++ b/includes/db/LoadBalancer.php @@ -13,7 +13,7 @@ class LoadBalancer { /* private */ var $mServers, $mConns, $mLoads, $mGroupLoads; /* private */ var $mFailFunction, $mErrorConnection; - /* private */ var $mReadIndex, $mLastIndex, $mAllowLagged; + /* private */ var $mReadIndex, $mAllowLagged; /* private */ var $mWaitForPos, $mWaitTimeout; /* private */ var $mLaggedSlaveMode, $mLastError = 'Unknown error'; /* private */ var $mParentInfo, $mLagTimes; @@ -50,7 +50,6 @@ class LoadBalancer { 'local' => array(), 'foreignUsed' => array(), 'foreignFree' => array() ); - $this->mLastIndex = -1; $this->mLoads = array(); $this->mWaitForPos = false; $this->mLaggedSlaveMode = false; @@ -399,11 +398,20 @@ class LoadBalancer { /** * Get a connection by index * This is the main entry point for this class. + * @param int $i Database + * @param array $groups Query groups + * @param string $wiki Wiki ID */ public function &getConnection( $i, $groups = array(), $wiki = false ) { global $wgDBtype; wfProfileIn( __METHOD__ ); + if ( $i == DB_LAST ) { + throw new MWException( 'Attempt to call ' . __METHOD__ . ' with deprecated server index DB_LAST' ); + } elseif ( $i === null || $i === false ) { + throw new MWException( 'Attempt to call ' . __METHOD__ . ' with invalid server index' ); + } + if ( $wiki === wfWikiID() ) { $wiki = false; } @@ -433,19 +441,13 @@ class LoadBalancer { # Operation-based index if ( $i == DB_SLAVE ) { $i = $this->getReaderIndex( false, $wiki ); - } elseif ( $i == DB_LAST ) { - # Just use $this->mLastIndex, which should already be set - $i = $this->mLastIndex; - if ( $i === -1 ) { - # Oh dear, not set, best to use the writer for safety - wfDebug( "Warning: DB_LAST used when there was no previous index\n" ); - $i = $this->getWriterIndex(); + # Couldn't find a working server in getReaderIndex()? + if ( $i === false ) { + $this->mLastError = 'No working slave server: ' . $this->mLastError; + $this->reportConnectionError( $this->mErrorConnection ); + return false; } } - # Couldn't find a working server in getReaderIndex()? - if ( $i === false ) { - $this->reportConnectionError( $this->mErrorConnection ); - } # Now we have an explicit index into the servers array $conn = $this->openConnection( $i, $wiki ); @@ -525,7 +527,7 @@ class LoadBalancer { } else { $server = $this->mServers[$i]; $server['serverIndex'] = $i; - $conn = $this->reallyOpenConnection( $server ); + $conn = $this->reallyOpenConnection( $server, false ); if ( $conn->isOpen() ) { $this->mConns['local'][$i][0] = $conn; } else { @@ -534,7 +536,6 @@ class LoadBalancer { $conn = false; } } - $this->mLastIndex = $i; wfProfileOut( __METHOD__ ); return $conn; } @@ -576,9 +577,8 @@ class LoadBalancer { $oldWiki = key( $this->mConns['foreignFree'][$i] ); if ( !$conn->selectDB( $dbName ) ) { - global $wguname; $this->mLastError = "Error selecting database $dbName on server " . - $conn->getServer() . " from client host {$wguname['nodename']}\n"; + $conn->getServer() . " from client host " . wfHostname() . "\n"; $this->mErrorConnection = $conn; $conn = false; } else { @@ -598,6 +598,7 @@ class LoadBalancer { $this->mErrorConnection = $conn; $conn = false; } else { + $conn->tablePrefix( $prefix ); $this->mConns['foreignUsed'][$i][$wiki] = $conn; wfDebug( __METHOD__.": opened new connection for $i/$wiki\n" ); } @@ -661,31 +662,27 @@ class LoadBalancer { function reportConnectionError( &$conn ) { wfProfileIn( __METHOD__ ); - # Prevent infinite recursion - - static $reporting = false; - if ( !$reporting ) { - $reporting = true; - if ( !is_object( $conn ) ) { - // No last connection, probably due to all servers being too busy - $conn = new Database; - if ( $this->mFailFunction ) { - $conn->failFunction( $this->mFailFunction ); - $conn->reportConnectionError( $this->mLastError ); - } else { - // If all servers were busy, mLastError will contain something sensible - throw new DBConnectionError( $conn, $this->mLastError ); - } + + if ( !is_object( $conn ) ) { + // No last connection, probably due to all servers being too busy + wfLogDBError( "LB failure with no last connection\n" ); + $conn = new Database; + if ( $this->mFailFunction ) { + $conn->failFunction( $this->mFailFunction ); + $conn->reportConnectionError( $this->mLastError ); } else { - if ( $this->mFailFunction ) { - $conn->failFunction( $this->mFailFunction ); - } else { - $conn->failFunction( false ); - } - $server = $conn->getProperty( 'mServer' ); - $conn->reportConnectionError( "{$this->mLastError} ({$server})" ); + // If all servers were busy, mLastError will contain something sensible + throw new DBConnectionError( $conn, $this->mLastError ); + } + } else { + if ( $this->mFailFunction ) { + $conn->failFunction( $this->mFailFunction ); + } else { + $conn->failFunction( false ); } - $reporting = false; + $server = $conn->getProperty( 'mServer' ); + wfLogDBError( "Connection error: {$this->mLastError} ({$server})\n" ); + $conn->reportConnectionError( "{$this->mLastError} ({$server})" ); } wfProfileOut( __METHOD__ ); } diff --git a/includes/db/LoadMonitor.php b/includes/db/LoadMonitor.php index 8e16f1a1..929ab2b9 100644 --- a/includes/db/LoadMonitor.php +++ b/includes/db/LoadMonitor.php @@ -64,6 +64,9 @@ class LoadMonitor_MySQL implements LoadMonitor { $requestRate = 10; global $wgMemc; + if ( empty( $wgMemc ) ) + $wgMemc = wfGetMainCache(); + $masterName = $this->parent->getServerName( 0 ); $memcKey = wfMemcKey( 'lag_times', $masterName ); $times = $wgMemc->get( $memcKey ); -- cgit v1.2.2