From 31492fc3f1da06d04f128971288e8a7c595d00c3 Mon Sep 17 00:00:00 2001 From: Aryeh Gregor Date: Mon, 15 Jun 2009 18:39:41 +0000 Subject: [PATCH] Move some stuff from DatabaseBase to DatabaseMysql I moved a number of methods and marked them abstract in DatabaseBase, namely those that met the following criteria: 1) Used some mysql_* function, so would obviously fail on any other DB. 2) Were already implemented by all five non-MySQL subclasses. It would be reasonable to revisit these and make the less essential ones return a dummy value instead of being abstract. And document them. And move more methods that are MySQL-specific. But one thing at a time. I also split the classes Database, DatabaseMysql, and MySQLMasterPos to DatabaseMysql.php. This should theoretically have no practical changes, but it's a bunch of changed code, so it will possibly break something by accident. Followup to r51795. --- includes/AutoLoader.php | 6 +- includes/db/Database.php | 300 +++------------------------------- includes/db/DatabaseMysql.php | 297 +++++++++++++++++++++++++++++++++ 3 files changed, 320 insertions(+), 283 deletions(-) create mode 100644 includes/db/DatabaseMysql.php diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index 25d5bb2bb6..4ccfe5f448 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -302,10 +302,10 @@ $wgAutoloadLocalClasses = array( # includes/db 'Blob' => 'includes/db/Database.php', 'ChronologyProtector' => 'includes/db/LBFactory.php', - 'Database' => 'includes/db/Database.php', + 'Database' => 'includes/db/DatabaseMysql.php', 'DatabaseBase' => 'includes/db/Database.php', 'DatabaseMssql' => 'includes/db/DatabaseMssql.php', - 'DatabaseMysql' => 'includes/db/Database.php', + 'DatabaseMysql' => 'includes/db/DatabaseMysql.php', 'DatabaseOracle' => 'includes/db/DatabaseOracle.php', 'DatabasePostgres' => 'includes/db/DatabasePostgres.php', 'DatabaseSqlite' => 'includes/db/DatabaseSqlite.php', @@ -322,7 +322,7 @@ $wgAutoloadLocalClasses = array( 'LoadMonitor_MySQL' => 'includes/db/LoadMonitor.php', 'MSSQLField' => 'includes/db/DatabaseMssql.php', 'MySQLField' => 'includes/db/Database.php', - 'MySQLMasterPos' => 'includes/db/Database.php', + 'MySQLMasterPos' => 'includes/db/DatabaseMysql.php', 'ORABlob' => 'includes/db/DatabaseOracle.php', 'ORAResult' => 'includes/db/DatabaseOracle.php', 'PostgresField' => 'includes/db/DatabasePostgres.php', diff --git a/includes/db/Database.php b/includes/db/Database.php index 28027551c7..86ff1fc541 100644 --- a/includes/db/Database.php +++ b/includes/db/Database.php @@ -328,114 +328,7 @@ abstract class DatabaseBase { * @param $password String: database user password * @param $dbName String: database name */ - function open( $server, $user, $password, $dbName ) { - global $wgAllDBsAreLocalhost; - wfProfileIn( __METHOD__ ); - - # Test for missing mysql.so - # First try to load it - if (!@extension_loaded('mysql')) { - @dl('mysql.so'); - } - - # Fail now - # Otherwise we get a suppressed fatal error, which is very hard to track down - if ( !function_exists( 'mysql_connect' ) ) { - throw new DBConnectionError( $this, "MySQL functions missing, have you compiled PHP with the --with-mysql option?\n" ); - } - - # Debugging hack -- fake cluster - if ( $wgAllDBsAreLocalhost ) { - $realServer = 'localhost'; - } else { - $realServer = $server; - } - $this->close(); - $this->mServer = $server; - $this->mUser = $user; - $this->mPassword = $password; - $this->mDBname = $dbName; - - $success = false; - - wfProfileIn("dbconnect-$server"); - - # The kernel's default SYN retransmission period is far too slow for us, - # 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; - if ( ini_get( 'mysql.connect_timeout' ) <= 3 ) { - $numAttempts = 2; - } else { - $numAttempts = 1; - } - $this->installErrorHandler(); - for ( $i = 0; $i < $numAttempts && !$this->mConn; $i++ ) { - if ( $i > 1 ) { - usleep( 1000 ); - } - if ( $this->mFlags & DBO_PERSISTENT ) { - $this->mConn = mysql_pconnect( $realServer, $user, $password ); - } else { - # Create a new connection... - $this->mConn = mysql_connect( $realServer, $user, $password, true ); - } - if ($this->mConn === false) { - #$iplus = $i + 1; - #wfLogDBError("Connect loop error $iplus of $max ($server): " . mysql_errno() . " - " . mysql_error()."\n"); - } - } - $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 != '' && $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 - $success = (bool)$this->mConn; - } - - if ( $success ) { - $version = $this->getServerVersion(); - if ( version_compare( $version, '4.1' ) >= 0 ) { - // Tell the server we're communicating with it in UTF-8. - // This may engage various charset conversions. - global $wgDBmysql5; - if( $wgDBmysql5 ) { - $this->query( 'SET NAMES utf8', __METHOD__ ); - } - // Turn off strict mode - $this->query( "SET sql_mode = ''", __METHOD__ ); - } - - // Turn off strict mode if it is on - } else { - $this->reportConnectionError( $phpError ); - } - - $this->mOpened = $success; - wfProfileOut( __METHOD__ ); - return $success; - } + abstract function open( $server, $user, $password, $dbName ); protected function installErrorHandler() { $this->mPHPError = false; @@ -467,18 +360,7 @@ abstract class DatabaseBase { * * @return Bool operation success. true if already closed. */ - function close() - { - $this->mOpened = false; - if ( $this->mConn ) { - if ( $this->trxLevel() ) { - $this->immediateCommit(); - } - return mysql_close( $this->mConn ); - } else { - return true; - } - } + abstract function close(); /** * @param $error String: fallback error message, used if none is given by MySQL @@ -630,14 +512,7 @@ abstract class DatabaseBase { * @return Result object to feed to fetchObject, fetchRow, ...; or false on failure * @private */ - /*private*/ function doQuery( $sql ) { - if( $this->bufferResults() ) { - $ret = mysql_query( $sql, $this->mConn ); - } else { - $ret = mysql_unbuffered_query( $sql, $this->mConn ); - } - return $ret; - } + /*private*/ abstract function doQuery( $sql ); /** * @param $error String @@ -762,14 +637,7 @@ abstract class DatabaseBase { * Free a result object * @param $res Mixed: A SQL result */ - function freeResult( $res ) { - if ( $res instanceof ResultWrapper ) { - $res = $res->result; - } - if ( !@/**/mysql_free_result( $res ) ) { - throw new DBUnexpectedError( $this, "Unable to free MySQL result" ); - } - } + abstract function freeResult( $res ); /** * Fetch the next row from the given result object, in object form. @@ -780,16 +648,7 @@ abstract class DatabaseBase { * @return MySQL row object * @throws DBUnexpectedError Thrown if the database returns an error */ - function fetchObject( $res ) { - if ( $res instanceof ResultWrapper ) { - $res = $res->result; - } - @/**/$row = mysql_fetch_object( $res ); - if( $this->lastErrno() ) { - throw new DBUnexpectedError( $this, 'Error in fetchObject(): ' . htmlspecialchars( $this->lastError() ) ); - } - return $row; - } + abstract function fetchObject( $res ); /** * Fetch the next row from the given result object, in associative array @@ -799,43 +658,20 @@ abstract class DatabaseBase { * @return MySQL row object * @throws DBUnexpectedError Thrown if the database returns an error */ - function fetchRow( $res ) { - if ( $res instanceof ResultWrapper ) { - $res = $res->result; - } - @/**/$row = mysql_fetch_array( $res ); - if ( $this->lastErrno() ) { - throw new DBUnexpectedError( $this, 'Error in fetchRow(): ' . htmlspecialchars( $this->lastError() ) ); - } - return $row; - } + abstract function fetchRow( $res ); /** * Get the number of rows in a result object * @param $res Mixed: A SQL result */ - function numRows( $res ) { - if ( $res instanceof ResultWrapper ) { - $res = $res->result; - } - @/**/$n = mysql_num_rows( $res ); - if( $this->lastErrno() ) { - throw new DBUnexpectedError( $this, 'Error in numRows(): ' . htmlspecialchars( $this->lastError() ) ); - } - return $n; - } + abstract function numRows( $res ); /** * 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 ) { - $res = $res->result; - } - return mysql_num_fields( $res ); - } + abstract function numFields( $res ); /** * Get a field name in a result object @@ -844,12 +680,7 @@ abstract class DatabaseBase { * @param $res Mixed: A SQL result * @param $n Integer */ - function fieldName( $res, $n ) { - if ( $res instanceof ResultWrapper ) { - $res = $res->result; - } - return mysql_field_name( $res, $n ); - } + abstract function fieldName( $res, $n ); /** * Get the inserted value of an auto-increment row @@ -861,7 +692,7 @@ abstract class DatabaseBase { * $dbw->insert('page',array('page_id' => $id)); * $id = $dbw->insertId(); */ - function insertId() { return mysql_insert_id( $this->mConn ); } + abstract function insertId(); /** * Change the position of the cursor in a result object @@ -869,51 +700,25 @@ abstract class DatabaseBase { * @param $res Mixed: A SQL result * @param $row Mixed: Either MySQL row or ResultWrapper */ - function dataSeek( $res, $row ) { - if ( $res instanceof ResultWrapper ) { - $res = $res->result; - } - return mysql_data_seek( $res, $row ); - } + abstract function dataSeek( $res, $row ); /** * Get the last error number * See mysql_errno() */ - function lastErrno() { - if ( $this->mConn ) { - return mysql_errno( $this->mConn ); - } else { - return mysql_errno(); - } - } + abstract function lastErrno(); /** * Get a description of the last error * See mysql_error() for more details */ - function lastError() { - if ( $this->mConn ) { - # Even if it's non-zero, it can still be invalid - wfSuppressWarnings(); - $error = mysql_error( $this->mConn ); - if ( !$error ) { - $error = mysql_error(); - } - wfRestoreWarnings(); - } else { - $error = mysql_error(); - } - if( $error ) { - $error .= ' (' . $this->mServer . ')'; - } - return $error; - } + abstract function lastError(); + /** * Get the number of rows affected by the last write query * See mysql_affected_rows() for more details */ - function affectedRows() { return mysql_affected_rows( $this->mConn ); } + abstract function affectedRows(); /** * Simple UPDATE wrapper @@ -1258,18 +1063,7 @@ abstract class DatabaseBase { * @param $table * @param $field */ - function fieldInfo( $table, $field ) { - $table = $this->tableName( $table ); - $res = $this->query( "SELECT * FROM $table LIMIT 1" ); - $n = mysql_num_fields( $res->result ); - for( $i = 0; $i < $n; $i++ ) { - $meta = mysql_fetch_field( $res->result, $i ); - if( $field == $meta->name ) { - return new MySQLField($meta); - } - } - return false; - } + abstract function fieldInfo( $table, $field ); /** * mysql_field_type() wrapper @@ -1459,10 +1253,7 @@ abstract class DatabaseBase { /** * Change the current database */ - function selectDB( $db ) { - $this->mDBname = $db; - return mysql_select_db( $db, $this->mConn ); - } + abstract function selectDB( $db ); /** * Get the current DB name @@ -1638,9 +1429,7 @@ abstract class DatabaseBase { * @param $s String: to be slashed. * @return String: slashed string. */ - function strencode( $s ) { - return mysql_real_escape_string( $s, $this->mConn ); - } + abstract function strencode( $s ); /** * If it's a string, adds quotes and backslashes @@ -2128,33 +1917,12 @@ abstract class DatabaseBase { /** * @return String: Version information from the database */ - function getServerVersion() { - return mysql_get_server_info( $this->mConn ); - } + abstract function getServerVersion(); /** * Ping the server and try to reconnect if it there is no connection */ - function ping() { - if( !function_exists( 'mysql_ping' ) ) { - wfDebug( "Tried to call mysql_ping but this is ancient PHP version. Faking it!\n" ); - return true; - } - $ping = mysql_ping( $this->mConn ); - if ( $ping ) { - return true; - } - - // Need to reconnect manually in MySQL client 5.0.13+ - if ( version_compare( mysql_get_client_info(), '5.0.13', '>=' ) ) { - mysql_close( $this->mConn ); - $this->mOpened = false; - $this->mConn = false; - $this->open( $this->mServer, $this->mUser, $this->mPassword, $this->mDBname ); - return true; - } - return false; - } + abstract function ping(); /** * Get slave lag. @@ -2435,21 +2203,6 @@ abstract class DatabaseBase { } } -/** - * Database abstraction object for mySQL - * Inherit all methods and properties of Database::Database() - * - * @ingroup Database - * @see Database - */ -class DatabaseMysql extends DatabaseBase { - # Inherit all -} - -/** - * Legacy support: Database == DatabaseMysql - */ -class Database extends DatabaseMysql {} /****************************************************************************** * Utility classes @@ -2902,16 +2655,3 @@ class ResultWrapper implements Iterator { return $this->current() !== false; } } - -class MySQLMasterPos { - var $file, $pos; - - function __construct( $file, $pos ) { - $this->file = $file; - $this->pos = $pos; - } - - function __toString() { - return "{$this->file}/{$this->pos}"; - } -} diff --git a/includes/db/DatabaseMysql.php b/includes/db/DatabaseMysql.php new file mode 100644 index 0000000000..27371f8da2 --- /dev/null +++ b/includes/db/DatabaseMysql.php @@ -0,0 +1,297 @@ +bufferResults() ) { + $ret = mysql_query( $sql, $this->mConn ); + } else { + $ret = mysql_unbuffered_query( $sql, $this->mConn ); + } + return $ret; + } + + function open( $server, $user, $password, $dbName ) { + global $wgAllDBsAreLocalhost; + wfProfileIn( __METHOD__ ); + + # Test for missing mysql.so + # First try to load it + if (!@extension_loaded('mysql')) { + @dl('mysql.so'); + } + + # Fail now + # Otherwise we get a suppressed fatal error, which is very hard to track down + if ( !function_exists( 'mysql_connect' ) ) { + throw new DBConnectionError( $this, "MySQL functions missing, have you compiled PHP with the --with-mysql option?\n" ); + } + + # Debugging hack -- fake cluster + if ( $wgAllDBsAreLocalhost ) { + $realServer = 'localhost'; + } else { + $realServer = $server; + } + $this->close(); + $this->mServer = $server; + $this->mUser = $user; + $this->mPassword = $password; + $this->mDBname = $dbName; + + $success = false; + + wfProfileIn("dbconnect-$server"); + + # The kernel's default SYN retransmission period is far too slow for us, + # 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; + if ( ini_get( 'mysql.connect_timeout' ) <= 3 ) { + $numAttempts = 2; + } else { + $numAttempts = 1; + } + $this->installErrorHandler(); + for ( $i = 0; $i < $numAttempts && !$this->mConn; $i++ ) { + if ( $i > 1 ) { + usleep( 1000 ); + } + if ( $this->mFlags & DBO_PERSISTENT ) { + $this->mConn = mysql_pconnect( $realServer, $user, $password ); + } else { + # Create a new connection... + $this->mConn = mysql_connect( $realServer, $user, $password, true ); + } + if ($this->mConn === false) { + #$iplus = $i + 1; + #wfLogDBError("Connect loop error $iplus of $max ($server): " . mysql_errno() . " - " . mysql_error()."\n"); + } + } + $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 != '' && $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 + $success = (bool)$this->mConn; + } + + if ( $success ) { + $version = $this->getServerVersion(); + if ( version_compare( $version, '4.1' ) >= 0 ) { + // Tell the server we're communicating with it in UTF-8. + // This may engage various charset conversions. + global $wgDBmysql5; + if( $wgDBmysql5 ) { + $this->query( 'SET NAMES utf8', __METHOD__ ); + } + // Turn off strict mode + $this->query( "SET sql_mode = ''", __METHOD__ ); + } + + // Turn off strict mode if it is on + } else { + $this->reportConnectionError( $phpError ); + } + + $this->mOpened = $success; + wfProfileOut( __METHOD__ ); + return $success; + } + + function close() { + $this->mOpened = false; + if ( $this->mConn ) { + if ( $this->trxLevel() ) { + $this->immediateCommit(); + } + return mysql_close( $this->mConn ); + } else { + return true; + } + } + + function freeResult( $res ) { + if ( $res instanceof ResultWrapper ) { + $res = $res->result; + } + if ( !@/**/mysql_free_result( $res ) ) { + throw new DBUnexpectedError( $this, "Unable to free MySQL result" ); + } + } + + function fetchObject( $res ) { + if ( $res instanceof ResultWrapper ) { + $res = $res->result; + } + @/**/$row = mysql_fetch_object( $res ); + if( $this->lastErrno() ) { + throw new DBUnexpectedError( $this, 'Error in fetchObject(): ' . htmlspecialchars( $this->lastError() ) ); + } + return $row; + } + + function fetchRow( $res ) { + if ( $res instanceof ResultWrapper ) { + $res = $res->result; + } + @/**/$row = mysql_fetch_array( $res ); + if ( $this->lastErrno() ) { + throw new DBUnexpectedError( $this, 'Error in fetchRow(): ' . htmlspecialchars( $this->lastError() ) ); + } + return $row; + } + + function numRows( $res ) { + if ( $res instanceof ResultWrapper ) { + $res = $res->result; + } + @/**/$n = mysql_num_rows( $res ); + if( $this->lastErrno() ) { + throw new DBUnexpectedError( $this, 'Error in numRows(): ' . htmlspecialchars( $this->lastError() ) ); + } + return $n; + } + + function numFields( $res ) { + if ( $res instanceof ResultWrapper ) { + $res = $res->result; + } + return mysql_num_fields( $res ); + } + + function fieldName( $res, $n ) { + if ( $res instanceof ResultWrapper ) { + $res = $res->result; + } + return mysql_field_name( $res, $n ); + } + + function insertId() { return mysql_insert_id( $this->mConn ); } + + function dataSeek( $res, $row ) { + if ( $res instanceof ResultWrapper ) { + $res = $res->result; + } + return mysql_data_seek( $res, $row ); + } + + function lastErrno() { + if ( $this->mConn ) { + return mysql_errno( $this->mConn ); + } else { + return mysql_errno(); + } + } + + function lastError() { + if ( $this->mConn ) { + # Even if it's non-zero, it can still be invalid + wfSuppressWarnings(); + $error = mysql_error( $this->mConn ); + if ( !$error ) { + $error = mysql_error(); + } + wfRestoreWarnings(); + } else { + $error = mysql_error(); + } + if( $error ) { + $error .= ' (' . $this->mServer . ')'; + } + return $error; + } + + function affectedRows() { return mysql_affected_rows( $this->mConn ); } + + function fieldInfo( $table, $field ) { + $table = $this->tableName( $table ); + $res = $this->query( "SELECT * FROM $table LIMIT 1" ); + $n = mysql_num_fields( $res->result ); + for( $i = 0; $i < $n; $i++ ) { + $meta = mysql_fetch_field( $res->result, $i ); + if( $field == $meta->name ) { + return new MySQLField($meta); + } + } + return false; + } + + function selectDB( $db ) { + $this->mDBname = $db; + return mysql_select_db( $db, $this->mConn ); + } + + function strencode( $s ) { + return mysql_real_escape_string( $s, $this->mConn ); + } + + function ping() { + if( !function_exists( 'mysql_ping' ) ) { + wfDebug( "Tried to call mysql_ping but this is ancient PHP version. Faking it!\n" ); + return true; + } + $ping = mysql_ping( $this->mConn ); + if ( $ping ) { + return true; + } + + // Need to reconnect manually in MySQL client 5.0.13+ + if ( version_compare( mysql_get_client_info(), '5.0.13', '>=' ) ) { + mysql_close( $this->mConn ); + $this->mOpened = false; + $this->mConn = false; + $this->open( $this->mServer, $this->mUser, $this->mPassword, $this->mDBname ); + return true; + } + return false; + } + + function getServerVersion() { + return mysql_get_server_info( $this->mConn ); + } +} + +/** + * Legacy support: Database == DatabaseMysql + */ +class Database extends DatabaseMysql {} + +class MySQLMasterPos { + var $file, $pos; + + function __construct( $file, $pos ) { + $this->file = $file; + $this->pos = $pos; + } + + function __toString() { + return "{$this->file}/{$this->pos}"; + } +} -- 2.20.1