From ccd051bc44974ba32749c845bf5d9aa0ca96aa24 Mon Sep 17 00:00:00 2001 From: "physikerwelt (Moritz Schubotz)" Date: Sat, 20 Sep 2014 12:36:03 -0700 Subject: [PATCH] Add counter to DatabaseUpdater::setAppliedUpdates The key was constructed depending on the current time only. This method fails, if multiple updates are performed within one second. Bug: 71087 Change-Id: Id7e30298729b3abb1501a34fcc1ba4e45d2172f0 --- includes/installer/DatabaseUpdater.php | 4 +- .../installer/DatabaseUpdaterTest.php | 279 ++++++++++++++++++ 2 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 tests/phpunit/includes/installer/DatabaseUpdaterTest.php diff --git a/includes/installer/DatabaseUpdater.php b/includes/installer/DatabaseUpdater.php index 193d59209f..6e0c37fd64 100644 --- a/includes/installer/DatabaseUpdater.php +++ b/includes/installer/DatabaseUpdater.php @@ -31,6 +31,7 @@ require_once __DIR__ . '/../../maintenance/Maintenance.php'; * @since 1.17 */ abstract class DatabaseUpdater { + protected static $updateCounter = 0; /** * Array of updates to perform on the database @@ -460,7 +461,8 @@ abstract class DatabaseUpdater { if ( !$this->canUseNewUpdatelog() ) { return; } - $key = "updatelist-$version-" . time(); + $key = "updatelist-$version-" . time() . self::$updateCounter; + self::$updateCounter++; $this->db->insert( 'updatelog', array( 'ul_key' => $key, 'ul_value' => serialize( $updates ) ), __METHOD__ ); diff --git a/tests/phpunit/includes/installer/DatabaseUpdaterTest.php b/tests/phpunit/includes/installer/DatabaseUpdaterTest.php new file mode 100644 index 0000000000..29c3052d7c --- /dev/null +++ b/tests/phpunit/includes/installer/DatabaseUpdaterTest.php @@ -0,0 +1,279 @@ +setAppliedUpdates( "test", array() ); + $expected = "updatelist-test-" . time() . "0"; + $actual = $db->lastInsertData['ul_key']; + $this->assertEquals( $expected, $actual, var_export( $db->lastInsertData, true ) ); + $dbu->setAppliedUpdates( "test", array() ); + $expected = "updatelist-test-" . time() . "1"; + $actual = $db->lastInsertData['ul_key']; + $this->assertEquals( $expected, $actual, var_export( $db->lastInsertData, true ) ); + } +} + +class FakeDatabase extends DatabaseBase { + public $lastInsertTable; + public $lastInsertData; + + function __construct() { + } + + function clearFlag( $arg ) { + } + + function setFlag( $arg ) { + } + + public function insert( $table, $a, $fname = __METHOD__, $options = array() ) { + $this->lastInsertTable = $table; + $this->lastInsertData = $a; + } + + /** + * Get the type of the DBMS, as it appears in $wgDBtype. + * + * @return string + */ + function getType() { + // TODO: Implement getType() method. + } + + /** + * Open a connection to the database. Usually aborts on failure + * + * @param string $server Database server host + * @param string $user Database user name + * @param string $password Database user password + * @param string $dbName Database name + * @return bool + * @throws DBConnectionError + */ + function open( $server, $user, $password, $dbName ) { + // TODO: Implement open() method. + } + + /** + * Fetch the next row from the given result object, in object form. + * Fields can be retrieved with $row->fieldname, with fields acting like + * member variables. + * If no more rows are available, false is returned. + * + * @param ResultWrapper|stdClass $res Object as returned from DatabaseBase::query(), etc. + * @return stdClass|bool + * @throws DBUnexpectedError Thrown if the database returns an error + */ + function fetchObject( $res ) { + // TODO: Implement fetchObject() method. + } + + /** + * Fetch the next row from the given result object, in associative array + * form. Fields are retrieved with $row['fieldname']. + * If no more rows are available, false is returned. + * + * @param ResultWrapper $res Result object as returned from DatabaseBase::query(), etc. + * @return array|bool + * @throws DBUnexpectedError Thrown if the database returns an error + */ + function fetchRow( $res ) { + // TODO: Implement fetchRow() method. + } + + /** + * Get the number of rows in a result object + * + * @param mixed $res A SQL result + * @return int + */ + function numRows( $res ) { + // TODO: Implement numRows() method. + } + + /** + * Get the number of fields in a result object + * @see http://www.php.net/mysql_num_fields + * + * @param mixed $res A SQL result + * @return int + */ + function numFields( $res ) { + // TODO: Implement numFields() method. + } + + /** + * Get a field name in a result object + * @see http://www.php.net/mysql_field_name + * + * @param mixed $res A SQL result + * @param int $n + * @return string + */ + function fieldName( $res, $n ) { + // TODO: Implement fieldName() method. + } + + /** + * Get the inserted value of an auto-increment row + * + * The value inserted should be fetched from nextSequenceValue() + * + * Example: + * $id = $dbw->nextSequenceValue( 'page_page_id_seq' ); + * $dbw->insert( 'page', array( 'page_id' => $id ) ); + * $id = $dbw->insertId(); + * + * @return int + */ + function insertId() { + // TODO: Implement insertId() method. + } + + /** + * Change the position of the cursor in a result object + * @see http://www.php.net/mysql_data_seek + * + * @param mixed $res A SQL result + * @param int $row + */ + function dataSeek( $res, $row ) { + // TODO: Implement dataSeek() method. + } + + /** + * Get the last error number + * @see http://www.php.net/mysql_errno + * + * @return int + */ + function lastErrno() { + // TODO: Implement lastErrno() method. + } + + /** + * Get a description of the last error + * @see http://www.php.net/mysql_error + * + * @return string + */ + function lastError() { + // TODO: Implement lastError() method. + } + + /** + * mysql_fetch_field() wrapper + * Returns false if the field doesn't exist + * + * @param string $table Table name + * @param string $field Field name + * + * @return Field + */ + function fieldInfo( $table, $field ) { + // TODO: Implement fieldInfo() method. + } + + /** + * Get information about an index into an object + * @param string $table Table name + * @param string $index Index name + * @param string $fname Calling function name + * @return mixed Database-specific index description class or false if the index does not exist + */ + function indexInfo( $table, $index, $fname = __METHOD__ ) { + // TODO: Implement indexInfo() method. + } + + /** + * Get the number of rows affected by the last write query + * @see http://www.php.net/mysql_affected_rows + * + * @return int + */ + function affectedRows() { + // TODO: Implement affectedRows() method. + } + + /** + * Wrapper for addslashes() + * + * @param string $s String to be slashed. + * @return string Slashed string. + */ + function strencode( $s ) { + // TODO: Implement strencode() method. + } + + /** + * Returns a wikitext link to the DB's website, e.g., + * return "[http://www.mysql.com/ MySQL]"; + * Should at least contain plain text, if for some reason + * your database has no website. + * + * @return string Wikitext of a link to the server software's web site + */ + function getSoftwareLink() { + // TODO: Implement getSoftwareLink() method. + } + + /** + * A string describing the current software version, like from + * mysql_get_server_info(). + * + * @return string Version information from the database server. + */ + function getServerVersion() { + // TODO: Implement getServerVersion() method. + } + + /** + * Closes underlying database connection + * @since 1.20 + * @return bool Whether connection was closed successfully + */ + protected function closeConnection() { + // TODO: Implement closeConnection() method. + } + + /** + * The DBMS-dependent part of query() + * + * @param string $sql SQL query. + * @return ResultWrapper|bool Result object to feed to fetchObject, + * fetchRow, ...; or false on failure + */ + protected function doQuery( $sql ) { + // TODO: Implement doQuery() method. + } +} + +class FakeDatabaseUpdater extends DatabaseUpdater { + function __construct( $db ) { + $this->db = $db; + self::$updateCounter = 0; + } + + /** + * Get an array of updates to perform on the database. Should return a + * multi-dimensional array. The main key is the MediaWiki version (1.12, + * 1.13...) with the values being arrays of updates, identical to how + * updaters.inc did it (for now) + * + * @return array + */ + protected function getCoreUpdateList() { + return array(); + } + + public function canUseNewUpdatelog() { + return true; + } + + public function setAppliedUpdates( $version, $updates ) { + parent::setAppliedUpdates( $version, $updates ); + } +} -- 2.20.1