3 A class to upgrade the database schema automagically
4 for MediaWIki itself, as well as for extensions
5 (c) 2006 by Magnus Manske
10 First, copy AdminSettings.sample to AdminSettings.php and set the correct values there.
12 $wgAllowUpgrader = true ;
16 In your extension, add something like:
17 $wgUpgrader->setUpgradeFunction ( "MyExtension" , "MyExtensionUpgradeFunction" ) ;
19 function MyExtensionUpgradeFunction ( &$upgrader ) {
21 # Does this installation need to be upgraded to V1.2.3.4?
22 if ( $upgrader->needsUpgrade ( "MyExtension" , "1.2.3.4" ) ) {
23 $upgrader->startTransaction() ;
24 # Do something using the database
26 $upgrader->endTransaction() ;
29 # Repeat this for every upgradable version, lowest version first, highest last
30 # if ( $upgrader->needsUpgrade ( "MyExtension" , "1.2.3.5" ) ) { ... }
34 The upgrade function for MediaWiki itself is at the end of Setup.php
40 var $data = array () ;
41 var $upgraders = array () ;
42 var $dba = NULL ; # Database, administration mode
43 var $data_loaded = false ;
44 var $db_transaction = false ;
48 function loadVersionInfoFromDatabase () {
49 global $wgAllowUpgrader ;
50 if ( !$wgAllowUpgrader ) return ;
51 if ( $this->data_loaded
) return ;
53 $fname = "Upgrader::loadVersionInfoFromDatabase" ;
54 $this->dbr
=& wfGetDB( DB_SLAVE
);
56 # Upgrade to upgrader :-)
57 if ( !$this->dbr
->tableExists ( "softwareversions" ) ) {
58 $this->prepareAdminDB () ;
59 $table = $this->dba
->tableName( "softwareversions" );
60 $sql = "CREATE TABLE {$table} (
61 `sv_part` VARCHAR( 128 ) NOT NULL ,
62 `sv_version` VARCHAR( 128 ) NOT NULL ,
63 PRIMARY KEY ( `sv_part` )
65 $this->dba
->query ( $sql , $fname."-1" ) ;
67 # At this point, we wait until the table creation
68 # has progressed to the $dbr slave
69 while ( !$this->dbr
->tableExists ( "softwareversions" ) ) {
74 $res = $this->dbr
->select (
80 # Read existing versions
81 while ( $o = $this->dbr
->fetchObject( $res ) ) {
82 $this->data
[strtolower($o->sv_part
)] = $this->makeVersionArray ( $o->sv_version
) ;
84 $this->dbr
->freeResult( $res );
86 $this->data_loaded
= true ;
90 * Adds an upgrade function to the list, to be evoked by run() later
92 function setUpgradeFunction ( $part , $function ) {
93 $this->upgraders
[$part] = $function ;
97 * Evokes the upgrade functions
100 global $wgAllowUpgrader ;
101 if ( !$wgAllowUpgrader ) return ;
103 # Running through the upgrading functions
104 foreach ( $this->upgraders
AS $part => $function ) {
105 $function ( $this ) ;
110 * Tells the caller if the given version is greater than the current one
111 @return bool TRUE if $part needs updating, FALSE otherwise
113 function needsUpgrade ( $part , $version ) {
114 $this->loadVersionInfoFromDatabase () ;
115 $currentversion = $this->getCurrentVersion ( $part ) ;
116 $this->lastcurrentversion
= $currentversion ;
117 $this->lastpart
= $part ;
118 $this->lastnewversion
= $version ;
119 return $this->compareVersions ( $currentversion , $version ) ;
123 * Initializes the transaction to update the database
125 function startTransaction () {
126 if ( $this->db_transaction
) # Already transaction in progress
129 # Open admin-access db
130 $this->prepareAdminDB() ;
131 $this->dba
->begin() ; # Begin transaction
132 $this->db_transaction
= true ;
136 * Finalizes the transaction to update the database
137 * Does a last paranoia check
139 function endTransaction () {
140 if ( !$this->db_transaction
)
142 if ( $this->dba
== NULL )
145 $fname = "Upgrader::endTransaction" ;
147 if ( $this->stillCurrentVersion() ) {
148 # Version has not changed since initial lookup; so we're the only one updating, hopefully
150 # Set new version string
151 $v = $this->makeVersionArray ( $this->lastnewversion
) ;
154 array ( "sv_part" => $this->lastpart
) ,
160 "sv_part" => strtolower ( $this->lastpart
) ,
161 "sv_version" => implode ( "." , $v ) ,
165 $this->data
[strtolower($this->lastpart
)] = $v ;
167 $this->dba
->commit($fname) ;
168 print $this->lastpart
. " has been upgraded to version " . implode ( "." , $v ) . "<br/>\n" ;
170 # Version has changes, so someone probably did the update already
171 $this->dba
->rollback($fname);
173 $this->db_transaction
= false ;
177 * Checks if the current version in the write table is still the same
178 * Multiple upgrading paranoia
181 function stillCurrentVersion () {
182 # Check if this is really, *really* the correct version in the writing DB
183 # Using new database to go around the transaction
184 $dbw =& wfGetDB( DB_MASTER
);
185 $res = $dbw->select (
188 array ( "sv_part" => $this->lastpart
) ,
189 "Upgrader::setLock" ) ;
191 if ( $res != NULL ) {
192 if ( $o = $dbw->fetchObject( $res ) ) {
193 $dbw->freeResult( $res );
194 $v = $o->sv_version
;
199 if ( $this->compareVersions ( $v , $this->lastcurrentversion
) OR
200 $this->compareVersions ( $this->lastcurrentversion
, $v ) ) {
201 $this->error ( "Something funny happened on the way to the moon." ) ;
208 * Open admin access to database
210 function prepareAdminDB () {
211 if ( $this->dba
!= NULL )
214 @include_once
( "AdminSettings.php" ) ;
215 if ( !isset ( $wgDBadminuser ) ) {
216 $this->error ( "You'll need to set up AdminSettings.php for automatic upgrades!" ) ;
220 global $wgDBserver , $wgDBname ;
221 $this->dba
= new Database ( $wgDBserver , $wgDBadminuser , $wgDBadminpassword , $wgDBname ) ;
225 * Get the current version of a part
228 function getCurrentVersion ( $part ) {
229 $part = strtolower ( $part ) ;
230 if ( isset ( $this->data
[$part] ) ) {
231 return $this->data
[$part] ;
233 # Not set yet, returning version 0.0.0.0
234 return $this->makeVersionArray ( "0.0.0.0" ) ;
239 * Compares versions, style 1.2.3.4
240 * Takes arrays or strings (which will be automatically converted)
241 @return bool TRUE if $v1 < $v2
243 function compareVersions ( $v1 , $v2 ) {
244 if ( !is_array ( $v1 ) )
245 $v1 = $this->makeVersionArray ( $v1 ) ;
246 if ( !is_array ( $v2 ) )
247 $v2 = $this->makeVersionArray ( $v2 ) ;
250 while ( count ( $v1 ) > 0 ) {
251 $a1 = array_shift ( $v1 ) ;
252 $a2 = array_shift ( $v2 ) ;
263 * Converts a version string ("1.2.3.4") to an array
264 * Ensures exactly four elements (fills up with "0" if necessary)
265 @return array like [ "1" , "2" , "3" , "4" ]
267 function makeVersionArray ( $v ) {
269 if ( !is_array ( $v ) ) {
270 $v = explode ( "." , $v ) ;
272 foreach ( $v AS $x ) {
274 if ( !is_numeric ( $x ) )
282 # Chop/stretch to length four
283 while ( count ( $ret ) > 4 )
285 while ( count ( $ret ) < 4 )
291 * Print an error and die a horrible death!
293 function error ( $e ) {
294 print $e . "<br/>\n" ;
298 } # End of class "Upgrader"
300 $wgUpgrader = new Upgrader
;