Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 122
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
MysqlUpdater
0.00% covered (danger)
0.00%
0 / 121
0.00% covered (danger)
0.00%
0 / 7
420
0.00% covered (danger)
0.00%
0 / 1
 getCoreUpdateList
0.00% covered (danger)
0.00%
0 / 64
0.00% covered (danger)
0.00%
0 / 1
2
 getInitialUpdateKeys
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 getSchemaVars
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 dropDefault
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
30
 setDefault
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 changeTableOption
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
 migrateSearchindex
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
42
1<?php
2
3/**
4 * MySQL-specific updater.
5 *
6 * @license GPL-2.0-or-later
7 * @file
8 * @ingroup Installer
9 */
10
11namespace MediaWiki\Installer;
12
13use FixInconsistentRedirects;
14use FixWrongPasswordPrefixes;
15use MediaWiki\Maintenance\FixAutoblockLogTitles;
16use MigrateExternallinks;
17use MigrateRevisionActorTemp;
18use MigrateRevisionCommentTemp;
19use PopulateUserIsTemp;
20use UpdateRestrictions;
21
22/**
23 * Mysql update list and mysql-specific update functions.
24 *
25 * @ingroup Installer
26 * @since 1.17
27 * @property \Wikimedia\Rdbms\DatabaseMySQL $db
28 */
29class MysqlUpdater extends DatabaseUpdater {
30    /** @inheritDoc */
31    protected function getCoreUpdateList() {
32        return [
33            // 1.39
34            [ 'addTable', 'user_autocreate_serial', 'patch-user_autocreate_serial.sql' ],
35            [ 'modifyField', 'ipblocks_restrictions', 'ir_ipb_id', 'patch-ipblocks_restrictions-ir_ipb_id.sql' ],
36            [ 'modifyField', 'ipblocks', 'ipb_id', 'patch-ipblocks-ipb_id.sql' ],
37            [ 'modifyField', 'user', 'user_editcount', 'patch-user-user_editcount.sql' ],
38            [ 'runMaintenance', MigrateRevisionActorTemp::class ],
39            [ 'dropTable', 'revision_actor_temp' ],
40            [ 'runMaintenance', UpdateRestrictions::class ],
41            [ 'dropField', 'page', 'page_restrictions', 'patch-page-drop-page_restrictions.sql' ],
42            [ 'migrateTemplatelinks' ],
43            [ 'modifyField', 'templatelinks', 'tl_namespace', 'patch-templatelinks-tl_title-nullable.sql' ],
44            [ 'dropField', 'templatelinks', 'tl_title', 'patch-templatelinks-drop-tl_title.sql' ],
45
46            // 1.40
47            [ 'addField', 'externallinks', 'el_to_path', 'patch-externallinks-el_to_path.sql' ],
48
49            // 1.41
50            [ 'addField', 'user', 'user_is_temp', 'patch-user-user_is_temp.sql' ],
51            [ 'runMaintenance', MigrateRevisionCommentTemp::class ],
52            [ 'dropTable', 'revision_comment_temp' ],
53            [ 'runMaintenance', MigrateExternallinks::class ],
54            [ 'modifyField', 'externallinks', 'el_to', 'patch-externallinks-el_to_default.sql' ],
55            [ 'addField', 'pagelinks', 'pl_target_id', 'patch-pagelinks-target_id.sql' ],
56            [ 'dropField', 'externallinks', 'el_to', 'patch-externallinks-drop-el_to.sql' ],
57            [ 'runMaintenance', FixInconsistentRedirects::class ],
58            [ 'modifyField', 'image', 'img_size', 'patch-image-img_size_to_bigint.sql' ],
59            [ 'modifyField', 'filearchive', 'fa_size', 'patch-filearchive-fa_size_to_bigint.sql' ],
60            [ 'modifyField', 'oldimage', 'oi_size', 'patch-oldimage-oi_size_to_bigint.sql' ],
61            [ 'modifyField', 'uploadstash', 'us_size', 'patch-uploadstash-us_size_to_bigint.sql' ],
62
63            // 1.42
64            [ 'addField', 'user_autocreate_serial', 'uas_year', 'patch-user_autocreate_serial-uas_year.sql' ],
65            [ 'addTable', 'block_target', 'patch-block_target.sql' ],
66            [ 'dropIndex', 'categorylinks', 'cl_collation_ext', 'patch-drop-cl_collation_ext.sql' ],
67            [ 'runMaintenance', PopulateUserIsTemp::class ],
68            [ 'dropIndex', 'sites', 'site_type', 'patch-sites-drop_indexes.sql' ],
69            [ 'dropIndex', 'iwlinks', 'iwl_prefix_from_title', 'patch-iwlinks-drop-iwl_prefix_from_title.sql' ],
70
71            // 1.43
72            [ 'migratePagelinks' ],
73            [ 'modifyField', 'revision', 'rev_id', 'patch-revision-cleanup.sql' ],
74            [ 'modifyField', 'recentchanges', 'rc_id', 'patch-recentchanges-rc_id-bigint.sql' ],
75            [ 'modifyField', 'change_tag', 'ct_rc_id', 'patch-change_tag-ct_rc_id.sql' ],
76            [ 'runMaintenance', \MigrateBlocks::class ],
77            [ 'dropTable', 'ipblocks' ],
78            [ 'dropField', 'pagelinks', 'pl_title', 'patch-pagelinks-drop-pl_title.sql' ],
79            [ 'modifyField', 'page', 'page_links_updated', 'patch-page-page_links_updated-noinfinite.sql' ],
80            [ 'addPostDatabaseUpdateMaintenance', FixAutoblockLogTitles::class ],
81            [ 'changeTableOption', 'searchindex', 'CONVERT TO CHARACTER SET utf8mb4', 'utf8mb4' ],
82            [ 'migrateSearchindex' ],
83
84            // 1.44
85            [ 'addTable', 'file', 'patch-file.sql' ],
86            [ 'addField', 'categorylinks', 'cl_target_id', 'patch-categorylinks-target_id.sql' ],
87            [ 'addTable', 'collation', 'patch-collation.sql' ],
88            [ 'dropTable', 'module_deps' ],
89
90            // 1.45
91            [ 'addTable', 'existencelinks', 'patch-existencelinks.sql' ],
92            [ 'runMaintenance', FixWrongPasswordPrefixes::class ],
93            [ 'addIndex', 'categorylinks', 'cl_timestamp_id', 'patch-categorylinks-cl_timestamp_id.sql' ],
94            [ 'migrateCategorylinks' ],
95            [ 'normalizeCollation' ],
96            [ 'modifyPrimaryKey', 'categorylinks', [ 'cl_from', 'cl_target_id' ], 'patch-categorylinks-pk.sql' ],
97            [ 'addIndex', 'recentchanges', 'rc_source_name_timestamp',
98                'patch-recentchanges-rc_source_name_timestamp.sql' ],
99            [ 'addIndex', 'recentchanges', 'rc_name_source_patrolled_timestamp',
100                'patch-recentchanges-rc_name_source_patrolled_timestamp.sql' ],
101            [ 'dropField', 'recentchanges', 'rc_new', 'patch-recentchanges-drop-rc_new.sql' ],
102            [ 'dropField', 'categorylinks', 'cl_to', 'patch-categorylinks-drop-cl_to-cl_collation.sql' ],
103
104            // 1.46
105            [ 'addTable', 'watchlist_label', 'patch-watchlist_label.sql' ],
106            [ 'dropField', 'recentchanges', 'rc_type', 'patch-recentchanges-drop-rc_type.sql' ],
107            [ 'dropField', 'archive', 'ar_sha1', 'patch-archive-drop-ar_sha1.sql' ],
108            [ 'dropField', 'revision', 'rev_sha1', 'patch-revision-drop-rev_sha1.sql' ],
109            [ 'dropField', 'objectcache', 'modtoken', 'patch-objectcache-drop-modtoken.sql' ],
110        ];
111    }
112
113    /** @inheritDoc */
114    protected function getInitialUpdateKeys() {
115        return [
116            'filearchive-fa_major_mime-patch-fa_major_mime-chemical.sql',
117            'image-img_major_mime-patch-img_major_mime-chemical.sql',
118            'oldimage-oi_major_mime-patch-oi_major_mime-chemical.sql',
119            'user_groups-ug_group-patch-ug_group-length-increase-255.sql',
120            'user_former_groups-ufg_group-patch-ufg_group-length-increase-255.sql',
121            'user_properties-up_property-patch-up_property.sql',
122        ];
123    }
124
125    /** @inheritDoc */
126    public function getSchemaVars() {
127        global $wgDBTableOptions;
128
129        $vars = [];
130        $vars['wgDBTableOptions'] = str_replace( 'TYPE', 'ENGINE', $wgDBTableOptions );
131        $vars['wgDBTableOptions'] = str_replace(
132            'CHARSET=mysql4',
133            'CHARSET=binary',
134            $vars['wgDBTableOptions']
135        );
136
137        return $vars;
138    }
139
140    /**
141     * Drops the default value from a field
142     *
143     * @since 1.36
144     * @param string $table
145     * @param string $field
146     */
147    protected function dropDefault( $table, $field ) {
148        $updateKey = "$table-$field-dropDefault";
149
150        if ( $this->updateRowExists( $updateKey ) ) {
151            return;
152        }
153
154        $info = $this->db->fieldInfo( $table, $field );
155        if ( $info && $info->defaultValue() !== false ) {
156            $this->output( "Removing '$table.$field' default value.\n" );
157            $table = $this->db->tableName( $table );
158            $ret = $this->db->query( "ALTER TABLE $table ALTER COLUMN $field DROP DEFAULT", __METHOD__ );
159
160            if ( $ret ) {
161                $this->insertUpdateRow( $updateKey );
162            }
163        }
164    }
165
166    /**
167     * Set a default value for a field
168     *
169     * @since 1.36
170     * @param string $table
171     * @param string $field
172     * @param mixed $default
173     */
174    protected function setDefault( $table, $field, $default ) {
175        $info = $this->db->fieldInfo( $table, $field );
176        if ( $info && $info->defaultValue() !== $default ) {
177            $this->output( "Changing '$table.$field' default value.\n" );
178            $table = $this->db->tableName( $table );
179            $this->db->query(
180                "ALTER TABLE $table ALTER COLUMN $field SET DEFAULT "
181                . $this->db->addQuotes( $default ), __METHOD__
182            );
183        }
184    }
185
186    /**
187     * Change the table options of a table
188     *
189     * @since 1.43
190     * @param string $table
191     * @param string $tableOption Raw table option that should already have been escaped !!!!
192     * @param string $updateName
193     */
194    protected function changeTableOption( string $table, string $tableOption, string $updateName ) {
195        $updateKey = "$table-tableoption-$updateName";
196        if ( $this->updateRowExists( $updateKey ) ) {
197            return;
198        }
199
200        $this->output( "Changing table options of '$table'.\n" );
201        $table = $this->db->tableName( $table );
202        $ret = $this->db->query(
203            "ALTER TABLE $table $tableOption",
204            __METHOD__
205        );
206
207        if ( $ret ) {
208            $this->insertUpdateRow( $updateKey );
209        }
210    }
211
212    protected function migrateSearchindex() {
213        $updateKey = 'searchindex-pk-titlelength';
214        if ( !$this->tableExists( 'searchindex' ) ) {
215            return;
216        }
217
218        $primaryIndexExists = $this->db->indexExists( 'searchindex', 'PRIMARY', __METHOD__ );
219        if ( $this->updateRowExists( $updateKey ) || $primaryIndexExists ) {
220            $this->output( "...searchindex table has already been migrated.\n" );
221            if ( !$this->updateRowExists( $updateKey ) ) {
222                $this->insertUpdateRow( $updateKey );
223            }
224            return;
225        }
226
227        $apply = $this->applyPatch( 'patch-searchindex-pk-titlelength.sql', false, '...migrating searchindex table' );
228
229        if ( $apply ) {
230            $this->insertUpdateRow( $updateKey );
231        }
232    }
233}
234
235/** @deprecated class alias since 1.42 */
236class_alias( MysqlUpdater::class, 'MysqlUpdater' );