From: Magnus Manske Date: Tue, 10 Jan 2006 12:53:43 +0000 (+0000) Subject: New toy: Automatic database upgrades (deactivated by default) X-Git-Tag: 1.6.0~640 X-Git-Url: https://git.cyclocoop.org/%7B%24www_url%7Dadmin/compta/exercices/journal.php?a=commitdiff_plain;h=65aa5bab1a0f742e2c8eb600d0361db1047bac38;p=lhc%2Fweb%2Fwiklou.git New toy: Automatic database upgrades (deactivated by default) Can create/upgrade database tables for MediaWiki and extensions based on internal version management. For HOWTO see the beginning of include/Upgrader.php --- diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index feb430e0cf..1f8103db8a 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -1812,4 +1812,10 @@ $wgFilterRobotsWL = false; */ $wgAllowCategorizedRecentChanges = false ; +/** + * Include upgrader + */ +$wgAllowUpgrader = false ; +require_once ( "Upgrader.php" ) ; + ?> diff --git a/includes/Setup.php b/includes/Setup.php index 12bc6f6283..5fcda81fcb 100644 --- a/includes/Setup.php +++ b/includes/Setup.php @@ -246,6 +246,7 @@ $wgMessageCache->initialise( $parserMemc, $wgUseDatabaseMessages, $wgMsgCacheExp wfProfileOut( $fname.'-MessageCache' ); + # # I guess the warning about UI switching might still apply... # @@ -311,6 +312,30 @@ foreach ( $wgExtensionFunctions as $func ) { wfDebug( "\n" ); $wgFullyInitialised = true; wfProfileOut( $fname.'-extensions' ); + + + +wfProfileIn( $fname.'-upgrades' ); + +# Upgrade MediaWiki +$wgUpgrader->setUpgradeFunction ( "MediaWiki" , "wfMediaWikiUpgrade" ) ; + +function wfMediaWikiUpgrade ( &$upgrader ) { + + if ( $upgrader->needsUpgrade ( "MediaWiki" , "1.6" ) ) { + $upgrader->startTransaction() ; + # Do something + $upgrader->endTransaction() ; + } + +} + + +# Upgrade software, if necessary +$wgUpgrader->run() ; + +wfProfileOut( $fname.'-upgrades' ); + wfProfileOut( $fname ); } diff --git a/includes/Upgrader.php b/includes/Upgrader.php new file mode 100644 index 0000000000..171cf6aadb --- /dev/null +++ b/includes/Upgrader.php @@ -0,0 +1,303 @@ +setUpgradeFunction ( "MyExtension" , "MyExtensionUpgradeFunction" ) ; + +function MyExtensionUpgradeFunction ( &$upgrader ) { + + # Does this installation need to be upgraded to V1.2.3.4? + if ( $upgrader->needsUpgrade ( "MyExtension" , "1.2.3.4" ) ) { + $upgrader->startTransaction() ; + # Do something using the database + # $upgrader->dba + $upgrader->endTransaction() ; + } + + # Repeat this for every upgradable version, lowest version first, highest last + # if ( $upgrader->needsUpgrade ( "MyExtension" , "1.2.3.5" ) ) { ... } + +} + +The upgrade function for MediaWiki itself is at the end of Setup.php + +*/ + +class Upgrader { + + var $data = array () ; + var $upgraders = array () ; + var $dba = NULL ; # Database, administration mode + var $data_loaded = false ; + var $db_transaction = false ; + var $dbr = NULL ; + var $dba = NULL ; + + function loadVersionInfoFromDatabase () { + global $wgAllowUpgrader ; + if ( !$wgAllowUpgrader ) return ; + if ( $this->data_loaded ) return ; + + $fname = "Upgrader::loadVersionInfoFromDatabase" ; + $this->dbr =& wfGetDB( DB_SLAVE ); + + # Upgrade to upgrader :-) + if ( !$this->dbr->tableExists ( "softwareversions" ) ) { + $this->prepareAdminDB () ; + $table = $this->dba->tableName( "softwareversions" ); + $sql = "CREATE TABLE {$table} ( + `sv_part` VARCHAR( 128 ) NOT NULL , + `sv_version` VARCHAR( 128 ) NOT NULL , + PRIMARY KEY ( `sv_part` ) + );" ; + $this->dba->query ( $sql , $fname."-1" ) ; + + # At this point, we wait until the table creation + # has progressed to the $dbr slave + while ( !$this->dbr->tableExists ( "softwareversions" ) ) { + sleep ( 1 ) ; + } + } + + $res = $this->dbr->select ( + "softwareversions" , + "*" , + array() , + $fname."-2" ) ; + + # Read existing versions + while ( $o = $this->dbr->fetchObject( $res ) ) { + $this->data[strtolower($o->sv_part)] = $this->makeVersionArray ( $o->sv_version ) ; + } + $this->dbr->freeResult( $res ); + + $this->data_loaded = true ; + } + + /** + * Adds an upgrade function to the list, to be evoked by run() later + */ + function setUpgradeFunction ( $part , $function ) { + $this->upgraders[$part] = $function ; + } + + /** + * Evokes the upgrade functions + */ + function run () { + global $wgAllowUpgrader ; + if ( !$wgAllowUpgrader ) return ; + + # Running through the upgrading functions + foreach ( $this->upgraders AS $part => $function ) { + $function ( $this ) ; + } + } + + /** + * Tells the caller if the given version is greater than the current one + @return bool TRUE if $part needs updating, FALSE otherwise + */ + function needsUpgrade ( $part , $version ) { + $this->loadVersionInfoFromDatabase () ; + $currentversion = $this->getCurrentVersion ( $part ) ; + $this->lastcurrentversion = $currentversion ; + $this->lastpart = $part ; + $this->lastnewversion = $version ; + return $this->compareVersions ( $currentversion , $version ) ; + } + + /** + * Initializes the transaction to update the database + */ + function startTransaction () { + if ( $this->db_transaction ) # Already transaction in progress + return ; + + # Open admin-access db + $this->prepareAdminDB() ; + $this->dba->begin() ; # Begin transaction + $this->db_transaction = true ; + } + + /** + * Finalizes the transaction to update the database + * Does a last paranoia check + */ + function endTransaction () { + if ( !$this->db_transaction ) + return ; + if ( $this->dba == NULL ) + return ; + + $fname = "Upgrader::endTransaction" ; + + if ( $this->stillCurrentVersion() ) { + # Version has not changed since initial lookup; so we're the only one updating, hopefully + + # Set new version string + $v = $this->makeVersionArray ( $this->lastnewversion ) ; + $this->dba->delete ( + "softwareversions", + array ( "sv_part" => $this->lastpart ) , + $fname . "-DELETE" + ) ; + $this->dba->insert ( + "softwareversions", + array ( + "sv_part" => strtolower ( $this->lastpart ) , + "sv_version" => implode ( "." , $v ) , + ) , + $fname . "-INSERT" + ) ; + $this->data[strtolower($this->lastpart)] = $v ; + + $this->dba->commit($fname) ; + print $this->lastpart . " has been upgraded to version " . implode ( "." , $v ) . "
\n" ; + } else { + # Version has changes, so someone probably did the update already + $this->dba->rollback($fname); + } + $this->db_transaction = false ; + } + + /** + * Checks if the current version in the write table is still the same + * Multiple upgrading paranoia + @return bool + */ + function stillCurrentVersion () { + # Check if this is really, *really* the correct version in the writing DB + # Using new database to go around the transaction + $dbw =& wfGetDB( DB_MASTER ); + $res = $dbw->select ( + "softwareversions" , + "*" , + array ( "sv_part" => $this->lastpart ) , + "Upgrader::setLock" ) ; + $v = "0.0.0.0" ; + if ( $res != NULL ) { + if ( $o = $dbw->fetchObject( $res ) ) { + $dbw->freeResult( $res ); + $v = $o->sv_version ; + } + } + + # Are they unequal? + if ( $this->compareVersions ( $v , $this->lastcurrentversion ) OR + $this->compareVersions ( $this->lastcurrentversion , $v ) ) { + $this->error ( "Something funny happened on the way to the moon." ) ; + return false ; + } + return true ; + } + + /** + * Open admin access to database + */ + function prepareAdminDB () { + if ( $this->dba != NULL ) + return ; + + @include_once ( "AdminSettings.php" ) ; + if ( !isset ( $wgDBadminuser ) ) { + $this->error ( "You'll need to set up AdminSettings.php for automatic upgrades!" ) ; + exit ( 1 ) ; + } + + global $wgDBserver , $wgDBname ; + $this->dba = new Database ( $wgDBserver , $wgDBadminuser , $wgDBadminpassword , $wgDBname ) ; + } + + /** + * Get the current version of a part + @return array + */ + function getCurrentVersion ( $part ) { + $part = strtolower ( $part ) ; + if ( isset ( $this->data[$part] ) ) { + return $this->data[$part] ; + } else { + # Not set yet, returning version 0.0.0.0 + return $this->makeVersionArray ( "0.0.0.0" ) ; + } + } + + /** + * Compares versions, style 1.2.3.4 + * Takes arrays or strings (which will be automatically converted) + @return bool TRUE if $v1 < $v2 + */ + function compareVersions ( $v1 , $v2 ) { + if ( !is_array ( $v1 ) ) + $v1 = $this->makeVersionArray ( $v1 ) ; + if ( !is_array ( $v2 ) ) + $v2 = $this->makeVersionArray ( $v2 ) ; + + # Compare versions + while ( count ( $v1 ) > 0 ) { + $a1 = array_shift ( $v1 ) ; + $a2 = array_shift ( $v2 ) ; + if ( $a1 < $a2 ) + return true ; + if ( $a1 > $a2 ) + return false ; + } + # Equal versions + return false ; + } + + /** + * Converts a version string ("1.2.3.4") to an array + * Ensures exactly four elements (fills up with "0" if necessary) + @return array like [ "1" , "2" , "3" , "4" ] + */ + function makeVersionArray ( $v ) { + # Convert to array + if ( !is_array ( $v ) ) { + $v = explode ( "." , $v ) ; + $ret = array () ; + foreach ( $v AS $x ) { + $x = trim ( $x ) ; + if ( !is_numeric ( $x ) ) + continue ; + $ret[] = $x ; + } + } else { + $ret = $v ; + } + + # Chop/stretch to length four + while ( count ( $ret ) > 4 ) + array_pop ( $ret ) ; + while ( count ( $ret ) < 4 ) + $ret[] = "0" ; + return $ret ; + } + + /** + * Print an error and die a horrible death! + */ + function error ( $e ) { + print $e . "
\n" ; + exit ( 1 ) ; + } + +} # End of class "Upgrader" + +$wgUpgrader = new Upgrader ; + + +?> \ No newline at end of file