24 require_once __DIR__ .
'/../../maintenance/Maintenance.php';
69 'DeleteDefaultMessages',
70 'PopulateRevisionLength',
71 'PopulateRevisionSha1',
73 'FixExtLinksProtocolRelative',
74 'PopulateFilearchiveSha1',
124 global $wgExtNewTables, $wgExtNewFields, $wgExtPGNewFields,
125 $wgExtPGAlteredFields, $wgExtNewIndexes, $wgExtModifiedFields;
127 # For extensions only, should be populated via hooks
128 # $wgDBtype should be checked to specifiy the proper file
129 $wgExtNewTables =
array();
130 $wgExtNewFields =
array();
131 $wgExtPGNewFields =
array();
132 $wgExtPGAlteredFields =
array();
133 $wgExtNewIndexes =
array();
134 $wgExtModifiedFields =
array();
142 if ( !defined(
'MEDIAWIKI_INSTALL' ) ) {
149 if ( !isset(
$vars[
'wgHooks'] ) || !isset(
$vars[
'wgHooks'][
'LoadExtensionSchemaUpdates'] ) ) {
153 $wgHooks[
'LoadExtensionSchemaUpdates'] =
$vars[
'wgHooks'][
'LoadExtensionSchemaUpdates'];
168 $class = ucfirst(
$type ) .
'Updater';
172 throw new MWException( __METHOD__ .
' called for unsupported $wgDBtype' );
181 public function getDB() {
190 public function output( $str ) {
191 if ( $this->maintenance->isQuiet() ) {
196 $str = htmlspecialchars( $str );
216 $this->extensionUpdates[] = $update;
229 $this->extensionUpdates[] =
array(
'addTable', $tableName, $sqlPath,
true );
240 $this->extensionUpdates[] =
array(
'addIndex', $tableName, $indexName, $sqlPath,
true );
252 $this->extensionUpdates[] =
array(
'addField', $tableName, $columnName, $sqlPath,
true );
264 $this->extensionUpdates[] =
array(
'dropField', $tableName, $columnName, $sqlPath,
true );
277 $this->extensionUpdates[] =
array(
'dropIndex', $tableName, $indexName, $sqlPath,
true );
288 $this->extensionUpdates[] =
array(
'dropTable', $tableName, $sqlPath,
true );
304 $sqlPath, $skipBothIndexExistWarning =
false
306 $this->extensionUpdates[] =
array(
311 $skipBothIndexExistWarning,
325 $this->extensionUpdates[] =
array(
'modifyField', $tableName, $fieldName, $sqlPath,
true );
336 return ( $this->db->tableExists( $tableName, __METHOD__ ) );
349 $this->postDatabaseUpdateMaintenance[] = $class;
377 $this->updatesSkipped =
array();
380 $func = $funcList[0];
382 $origParams = $funcList[2];
383 call_user_func_array( $func, $arg );
385 $this->updatesSkipped[] = $origParams;
394 public function doUpdates( $what =
array(
'core',
'extensions',
'stats' ) ) {
397 $this->db->begin( __METHOD__ );
398 $what = array_flip( $what );
399 $this->skipSchema = isset( $what[
'noschema'] ) || $this->fileHandle !==
null;
400 if ( isset( $what[
'core'] ) ) {
403 if ( isset( $what[
'extensions'] ) ) {
408 if ( isset( $what[
'stats'] ) ) {
414 if ( $this->fileHandle ) {
415 $this->skipSchema =
false;
420 $this->db->commit( __METHOD__ );
431 $updatesDone =
array();
435 $func = array_shift(
$params );
436 if ( !is_array( $func ) && method_exists( $this, $func ) ) {
437 $func =
array( $this, $func );
438 } elseif ( $passSelf ) {
439 array_unshift(
$params, $this );
443 if (
$ret !==
false ) {
444 $updatesDone[] = $origParams;
449 $this->updatesSkipped = array_merge( $this->updatesSkipped,
$updatesSkipped );
462 $key =
"updatelist-$version-" . time();
463 $this->db->insert(
'updatelog',
464 array(
'ul_key' => $key,
'ul_value' => serialize(
$updates ) ),
477 $row = $this->db->selectRow(
480 array(
'ul_key' => $key ),
496 $values =
array(
'ul_key' => $key );
498 $values[
'ul_value'] = $val;
500 $this->db->insert(
'updatelog', $values, __METHOD__,
'IGNORE' );
513 return $this->db->tableExists(
'updatelog', __METHOD__ ) &&
514 $this->db->fieldExists(
'updatelog',
'ul_value', __METHOD__ );
526 global $wgSharedDB, $wgSharedTables;
530 if ( $wgSharedDB ===
null || $this->shared ) {
534 return !in_array(
$name, $wgSharedTables );
546 global $wgExtNewFields, $wgExtNewTables, $wgExtModifiedFields,
551 foreach ( $wgExtNewTables
as $tableRecord ) {
553 'addTable', $tableRecord[0], $tableRecord[1],
true
557 foreach ( $wgExtNewFields
as $fieldRecord ) {
559 'addField', $fieldRecord[0], $fieldRecord[1],
560 $fieldRecord[2],
true
564 foreach ( $wgExtNewIndexes
as $fieldRecord ) {
566 'addIndex', $fieldRecord[0], $fieldRecord[1],
567 $fieldRecord[2],
true
571 foreach ( $wgExtModifiedFields
as $fieldRecord ) {
573 'modifyField', $fieldRecord[0], $fieldRecord[1],
574 $fieldRecord[2],
true
596 public function copyFile( $filename ) {
597 $this->db->sourceFile( $filename,
false,
false,
false,
598 array( $this,
'appendLine' )
614 if ( fwrite( $this->fileHandle,
$line ) ===
false ) {
629 protected function applyPatch(
$path, $isFullPath =
false, $msg =
null ) {
630 if ( $msg ===
null ) {
631 $msg =
"Applying $path patch";
633 if ( $this->skipSchema ) {
634 $this->
output(
"...skipping schema change ($msg).\n" );
639 $this->
output(
"$msg ..." );
641 if ( !$isFullPath ) {
644 if ( $this->fileHandle !==
null ) {
647 $this->db->sourceFile(
$path );
649 $this->
output(
"done.\n" );
662 protected function addTable(
$name, $patch, $fullpath =
false ) {
667 if ( $this->db->tableExists(
$name, __METHOD__ ) ) {
668 $this->
output(
"...$name table already exists.\n" );
670 return $this->
applyPatch( $patch, $fullpath,
"Creating $name table" );
685 protected function addField( $table, $field, $patch, $fullpath =
false ) {
686 if ( !$this->
doTable( $table ) ) {
690 if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
691 $this->
output(
"...$table table does not exist, skipping new field patch.\n" );
692 } elseif ( $this->db->fieldExists( $table, $field, __METHOD__ ) ) {
693 $this->
output(
"...have $field field in $table table.\n" );
695 return $this->
applyPatch( $patch, $fullpath,
"Adding $field field to table $table" );
710 protected function addIndex( $table, $index, $patch, $fullpath =
false ) {
711 if ( !$this->
doTable( $table ) ) {
715 if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
716 $this->
output(
"...skipping: '$table' table doesn't exist yet.\n" );
717 } elseif ( $this->db->indexExists( $table, $index, __METHOD__ ) ) {
718 $this->
output(
"...index $index already set on $table table.\n" );
720 return $this->
applyPatch( $patch, $fullpath,
"Adding index $index to table $table" );
735 protected function dropField( $table, $field, $patch, $fullpath =
false ) {
736 if ( !$this->
doTable( $table ) ) {
740 if ( $this->db->fieldExists( $table, $field, __METHOD__ ) ) {
741 return $this->
applyPatch( $patch, $fullpath,
"Table $table contains $field field. Dropping" );
743 $this->
output(
"...$table table does not contain $field field.\n" );
758 protected function dropIndex( $table, $index, $patch, $fullpath =
false ) {
759 if ( !$this->
doTable( $table ) ) {
763 if ( $this->db->indexExists( $table, $index, __METHOD__ ) ) {
764 return $this->
applyPatch( $patch, $fullpath,
"Dropping $index index from table $table" );
766 $this->
output(
"...$index key doesn't exist.\n" );
784 protected function renameIndex( $table, $oldIndex, $newIndex,
785 $skipBothIndexExistWarning, $patch, $fullpath =
false
787 if ( !$this->
doTable( $table ) ) {
792 if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
793 $this->
output(
"...skipping: '$table' table doesn't exist yet.\n" );
799 if ( $this->db->indexExists( $table, $newIndex, __METHOD__ ) ) {
800 $this->
output(
"...index $newIndex already set on $table table.\n" );
801 if ( !$skipBothIndexExistWarning &&
802 $this->db->indexExists( $table, $oldIndex, __METHOD__ )
804 $this->
output(
"...WARNING: $oldIndex still exists, despite it has " .
805 "been renamed into $newIndex (which also exists).\n" .
806 " $oldIndex should be manually removed if not needed anymore.\n" );
813 if ( !$this->db->indexExists( $table, $oldIndex, __METHOD__ ) ) {
814 $this->
output(
"...skipping: index $oldIndex doesn't exist.\n" );
823 "Renaming index $oldIndex into $newIndex to table $table"
838 public function dropTable( $table, $patch =
false, $fullpath =
false ) {
839 if ( !$this->
doTable( $table ) ) {
843 if ( $this->db->tableExists( $table, __METHOD__ ) ) {
844 $msg =
"Dropping table $table";
846 if ( $patch ===
false ) {
847 $this->
output(
"$msg ..." );
848 $this->db->dropTable( $table, __METHOD__ );
849 $this->
output(
"done.\n" );
851 return $this->
applyPatch( $patch, $fullpath, $msg );
854 $this->
output(
"...$table doesn't exist.\n" );
869 public function modifyField( $table, $field, $patch, $fullpath =
false ) {
870 if ( !$this->
doTable( $table ) ) {
874 $updateKey =
"$table-$field-$patch";
875 if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
876 $this->
output(
"...$table table does not exist, skipping modify field patch.\n" );
877 } elseif ( !$this->db->fieldExists( $table, $field, __METHOD__ ) ) {
878 $this->
output(
"...$field field does not exist in $table table, " .
879 "skipping modify field patch.\n" );
881 $this->
output(
"...$field in table $table already modified by patch $patch.\n" );
885 return $this->
applyPatch( $patch, $fullpath,
"Modifying $field field of table $table" );
895 global $wgLocalisationCacheConf;
896 # We can't guarantee that the user will be able to use TRUNCATE,
897 # but we know that DELETE is available to us
898 $this->
output(
"Purging caches..." );
899 $this->db->delete(
'objectcache',
'*', __METHOD__ );
900 if ( $wgLocalisationCacheConf[
'manualRecache'] ) {
911 $this->
output(
"...site_stats is populated..." );
912 $row = $this->db->selectRow(
'site_stats',
'*',
array(
'ss_row_id' => 1 ), __METHOD__ );
913 if ( $row ===
false ) {
914 $this->
output(
"data is missing! rebuilding...\n" );
915 } elseif ( isset( $row->site_stats ) && $row->ss_total_pages == -1 ) {
916 $this->
output(
"missing ss_total_pages, rebuilding...\n" );
918 $this->
output(
"done.\n" );
925 # Common updater functions
931 $activeUsers = $this->db->selectField(
'site_stats',
'ss_active_users',
false, __METHOD__ );
932 if ( $activeUsers == -1 ) {
933 $activeUsers = $this->db->selectField(
'recentchanges',
934 'COUNT( DISTINCT rc_user_text )',
935 array(
'rc_user != 0',
'rc_bot' => 0,
"rc_log_type != 'newusers'" ), __METHOD__
937 $this->db->update(
'site_stats',
938 array(
'ss_active_users' => intval( $activeUsers ) ),
939 array(
'ss_row_id' => 1 ), __METHOD__,
array(
'LIMIT' => 1 )
942 $this->
output(
"...ss_active_users user count set...\n" );
951 "Populating log_user_text field, printing progress markers. For large\n" .
952 "databases, you may want to hit Ctrl-C and do this manually with\n" .
953 "maintenance/populateLogUsertext.php.\n"
956 $task = $this->maintenance->runChild(
'PopulateLogUsertext' );
958 $this->
output(
"done.\n" );
968 "Populating log_search table, printing progress markers. For large\n" .
969 "databases, you may want to hit Ctrl-C and do this manually with\n" .
970 "maintenance/populateLogSearch.php.\n" );
972 $task = $this->maintenance->runChild(
'PopulateLogSearch' );
974 $this->
output(
"done.\n" );
983 $this->
output(
"...transcache tc_time already converted.\n" );
988 return $this->
applyPatch(
'patch-tc-timestamp.sql',
false,
989 "Converting tc_time from UNIX epoch to MediaWiki timestamp" );
996 global $wgCategoryCollation;
997 if ( $this->db->fieldExists(
'categorylinks',
'cl_collation', __METHOD__ ) ) {
998 if ( $this->db->selectField(
1001 'cl_collation != ' . $this->db->addQuotes( $wgCategoryCollation ),
1005 $this->
output(
"...collations up-to-date.\n" );
1010 $this->
output(
"Updating category collations..." );
1011 $task = $this->maintenance->runChild(
'UpdateCollation' );
1013 $this->
output(
"...done.\n" );
1021 if ( $this->db->tableExists(
'user_properties' ) ) {
1022 $cl = $this->maintenance->runChild(
'ConvertUserOptions',
'convertUserOptions.php' );
1024 $this->
output(
"done.\n" );
1035 $cl = $this->maintenance->runChild(
'RebuildLocalisationCache',
'rebuildLocalisationCache.php' );
1036 $this->
output(
"Rebuilding localisation cache...\n" );
1039 $this->
output(
"done.\n" );
1047 global $wgContentHandlerUseDB;
1049 if ( $wgContentHandlerUseDB ) {
1050 $this->
output(
"Turning off Content Handler DB fields for this part of upgrade.\n" );
1051 $this->holdContentHandlerUseDB = $wgContentHandlerUseDB;
1052 $wgContentHandlerUseDB =
false;
1060 global $wgContentHandlerUseDB;
1062 if ( $this->holdContentHandlerUseDB ) {
1063 $this->
output(
"Content Handler DB fields should be usable now.\n" );