Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
SchemaChangesHandler
n/a
0 / 0
n/a
0 / 0
12
n/a
0 / 0
 onLoadExtensionSchemaUpdates
n/a
0 / 0
n/a
0 / 0
12
1<?php
2
3namespace MediaWiki\CheckUser\HookHandler;
4
5use MediaWiki\CheckUser\Maintenance\FixTrailingSpacesInLogs;
6use MediaWiki\CheckUser\Maintenance\MoveLogEntriesFromCuChanges;
7use MediaWiki\CheckUser\Maintenance\PopulateCheckUserTable;
8use MediaWiki\CheckUser\Maintenance\PopulateCucActor;
9use MediaWiki\CheckUser\Maintenance\PopulateCucComment;
10use MediaWiki\CheckUser\Maintenance\PopulateCulActor;
11use MediaWiki\CheckUser\Maintenance\PopulateCulComment;
12use MediaWiki\Installer\Hook\LoadExtensionSchemaUpdatesHook;
13
14class SchemaChangesHandler implements LoadExtensionSchemaUpdatesHook {
15    /**
16     * @codeCoverageIgnore This is tested by installing or updating MediaWiki
17     * @inheritDoc
18     */
19    public function onLoadExtensionSchemaUpdates( $updater ) {
20        $base = __DIR__ . '/../../schema';
21        $maintenanceDb = $updater->getDB();
22        $dbType = $maintenanceDb->getType();
23        $isCUInstalled = $updater->tableExists( 'cu_changes' );
24
25        $updater->addExtensionTable( 'cu_changes', "$base/$dbType/tables-generated.sql" );
26
27        if ( $dbType === 'mysql' ) {
28            // 1.35
29            $updater->modifyExtensionField(
30                'cu_changes',
31                'cuc_id',
32                "$base/$dbType/patch-cu_changes-cuc_id-unsigned.sql"
33            );
34
35            // 1.38
36            $updater->addExtensionIndex(
37                'cu_changes',
38                'cuc_actor_ip_time',
39                "$base/$dbType/patch-cu_changes-actor-comment.sql"
40            );
41
42            // 1.39
43            $updater->modifyExtensionField(
44                'cu_changes',
45                'cuc_timestamp',
46                "$base/$dbType/patch-cu_changes-cuc_timestamp.sql"
47            );
48            $updater->addExtensionField(
49                'cu_log',
50                'cul_reason_id',
51                "$base/$dbType/patch-cu_log-comment_table_for_reason.sql"
52            );
53            $updater->addExtensionField(
54                'cu_log',
55                'cul_actor',
56                "$base/$dbType/patch-cu_log-actor.sql"
57            );
58        } elseif ( $dbType === 'sqlite' ) {
59            // 1.39
60            $updater->addExtensionIndex(
61                'cu_changes',
62                'cuc_actor_ip_time',
63                "$base/$dbType/patch-cu_changes-actor-comment.sql"
64            );
65            $updater->addExtensionField(
66                'cu_log',
67                'cul_reason_id',
68                "$base/$dbType/patch-cu_log-comment_table_for_reason.sql"
69            );
70            $updater->addExtensionField(
71                'cu_log',
72                'cul_actor',
73                "$base/$dbType/patch-cu_log-actor.sql"
74            );
75        } elseif ( $dbType === 'postgres' ) {
76            // 1.37
77            $updater->addExtensionUpdate( [ 'dropFkey', 'cu_log', 'cul_user' ] );
78            $updater->addExtensionUpdate( [ 'dropFkey', 'cu_log', 'cul_target_id' ] );
79            $updater->addExtensionUpdate( [ 'dropFkey', 'cu_changes', 'cuc_user' ] );
80            $updater->addExtensionUpdate( [ 'dropFkey', 'cu_changes', 'cuc_page_id' ] );
81
82            // 1.38
83            $updater->addExtensionUpdate(
84                [ 'addPgField', 'cu_changes', 'cuc_actor', 'INTEGER NOT NULL DEFAULT 0' ]
85            );
86            $updater->addExtensionUpdate(
87                [ 'addPgField', 'cu_changes', 'cuc_comment_id', 'INTEGER NOT NULL DEFAULT 0' ]
88            );
89            $updater->addExtensionUpdate(
90                [ 'setDefault', 'cu_changes', 'cuc_user_text', '' ]
91            );
92            $updater->addExtensionUpdate(
93                [ 'addPgIndex', 'cu_changes', 'cuc_actor_ip_time', '( cuc_actor, cuc_ip, cuc_timestamp )' ]
94            );
95
96            // 1.39
97            $updater->addExtensionIndex( 'cu_changes', 'cu_changes_pkey', "$base/$dbType/patch-cu_changes-pk.sql" );
98            $updater->addExtensionUpdate(
99                [ 'changeField', 'cu_changes', 'cuc_namespace', 'INT', 'cuc_namespace::INT DEFAULT 0' ]
100            );
101            if ( $maintenanceDb->fieldExists( 'cu_log', 'cuc_user' ) ) {
102                $updater->addExtensionUpdate(
103                    [ 'changeNullableField', 'cu_changes', 'cuc_user', 'NOT NULL', true ]
104                );
105            }
106            if ( $maintenanceDb->fieldExists( 'cu_log', 'cuc_user_text' ) ) {
107                $updater->addExtensionUpdate(
108                    [ 'changeField', 'cu_changes', 'cuc_user_text', 'VARCHAR(255)', '' ]
109                );
110                $updater->addExtensionUpdate(
111                    [ 'setDefault', 'cu_changes', 'cuc_user_text', '' ]
112                );
113            }
114            $updater->addExtensionUpdate(
115                [ 'changeField', 'cu_changes', 'cuc_actor', 'BIGINT', 'cuc_actor::BIGINT DEFAULT 0' ]
116            );
117            $updater->addExtensionUpdate(
118                [ 'changeField', 'cu_changes', 'cuc_comment_id', 'BIGINT', 'cuc_comment_id::BIGINT DEFAULT 0' ]
119            );
120            $updater->addExtensionUpdate(
121                [ 'changeField', 'cu_changes', 'cuc_minor', 'SMALLINT', 'cuc_minor::SMALLINT DEFAULT 0' ]
122            );
123            $updater->addExtensionUpdate(
124                [ 'changeNullableField', 'cu_changes', 'cuc_page_id', 'NOT NULL', true ]
125            );
126            $updater->addExtensionUpdate(
127                [ 'setDefault', 'cu_changes', 'cuc_page_id', 0 ]
128            );
129            $updater->addExtensionUpdate(
130                [ 'changeNullableField', 'cu_changes', 'cuc_timestamp', 'NOT NULL', true ]
131            );
132            $updater->addExtensionUpdate(
133                [ 'changeField', 'cu_changes', 'cuc_ip', 'VARCHAR(255)', '' ]
134            );
135            $updater->addExtensionUpdate(
136                [ 'setDefault', 'cu_changes', 'cuc_ip', '' ]
137            );
138            $updater->addExtensionUpdate(
139                [ 'changeField', 'cu_changes', 'cuc_ip_hex', 'VARCHAR(255)', '' ]
140            );
141            $updater->addExtensionUpdate(
142                [ 'setDefault', 'cu_changes', 'cuc_xff', '' ]
143            );
144            $updater->addExtensionUpdate(
145                [ 'changeField', 'cu_changes', 'cuc_xff_hex', 'VARCHAR(255)', '' ]
146            );
147            $updater->addExtensionUpdate(
148                [ 'changeField', 'cu_changes', 'cuc_private', 'TEXT', '' ]
149            );
150            $updater->addExtensionIndex( 'cu_log', 'cu_log_pkey', "$base/$dbType/patch-cu_log-pk.sql" );
151            $updater->addExtensionUpdate(
152                [ 'changeNullableField', 'cu_log', 'cul_timestamp', 'NOT NULL', true ]
153            );
154            if ( $maintenanceDb->fieldExists( 'cu_log', 'cul_user' ) ) {
155                $updater->addExtensionUpdate(
156                    [ 'changeNullableField', 'cu_log', 'cul_user', 'NOT NULL', true ]
157                );
158            }
159            $updater->addExtensionUpdate(
160                [ 'dropDefault', 'cu_log', 'cul_type' ]
161            );
162            $updater->addExtensionUpdate(
163                [ 'changeNullableField', 'cu_log', 'cul_target_id', 'NOT NULL', true ]
164            );
165            $updater->addExtensionUpdate(
166                [ 'setDefault', 'cu_log', 'cul_target_id', 0 ]
167            );
168            $updater->addExtensionUpdate(
169                [ 'dropDefault', 'cu_log', 'cul_target_text' ]
170            );
171            $updater->addExtensionUpdate(
172                [ 'addPgField', 'cu_log', 'cul_reason_id', 'INTEGER NOT NULL DEFAULT 0' ]
173            );
174            $updater->addExtensionUpdate(
175                [ 'addPgField', 'cu_log', 'cul_reason_plaintext_id', 'INTEGER NOT NULL DEFAULT 0' ]
176            );
177            $updater->addExtensionUpdate(
178                [ 'addPgField', 'cu_log', 'cul_actor', 'INTEGER NOT NULL DEFAULT 0' ]
179            );
180            $updater->addExtensionUpdate(
181                [ 'addPgIndex', 'cu_log', 'cul_actor_time', '( cul_actor, cul_timestamp )' ]
182            );
183        }
184
185        $updater->addExtensionUpdate( [
186            'runMaintenance',
187            PopulateCulActor::class,
188            'extensions/CheckUser/maintenance/populateCulActor.php'
189        ] );
190        $updater->addExtensionUpdate( [
191            'runMaintenance',
192            PopulateCulComment::class,
193            'extensions/CheckUser/maintenance/populateCulComment.php'
194        ] );
195        if ( $dbType === 'postgres' ) {
196            # For wikis which ran update.php after pulling the master branch of CheckUser between
197            #  4 June 2022 and 6 June 2022, the cul_reason_id and cul_reason_plaintext_id columns
198            #  were added but were by default NULL.
199            # This is needed for postgres installations that did the above. All other DB types
200            #  make the columns "NOT NULL" when removing the default.
201            $updater->addExtensionUpdate(
202                [ 'changeNullableField', 'cu_log', 'cul_reason_id', 'NOT NULL', true ]
203            );
204            $updater->addExtensionUpdate(
205                [ 'changeNullableField', 'cu_log', 'cul_reason_plaintext_id', 'NOT NULL', true ]
206            );
207        }
208
209        $updater->addExtensionUpdate( [
210            'runMaintenance',
211            PopulateCucActor::class,
212            'extensions/CheckUser/maintenance/populateCucActor.php'
213        ] );
214        $updater->addExtensionUpdate( [
215            'runMaintenance',
216            PopulateCucComment::class,
217            'extensions/CheckUser/maintenance/populateCucComment.php'
218        ] );
219
220        // 1.40
221        $updater->addExtensionTable(
222            'cu_log_event',
223            "$base/$dbType/patch-cu_log_event-def.sql"
224        );
225        $updater->addExtensionTable(
226            'cu_private_event',
227            "$base/$dbType/patch-cu_private_event-def.sql"
228        );
229        $updater->dropExtensionField(
230            'cu_log',
231            'cul_user',
232            "$base/$dbType/patch-cu_log-drop-cul_user.sql"
233        );
234        if (
235            $dbType !== 'sqlite' ||
236            $maintenanceDb->fieldExists( 'cu_log', 'cul_reason' )
237        ) {
238            // Only run this for SQLite if cul_reason exists,
239            //  as modifyExtensionField does not take into account
240            //  SQLite patches that use temporary tables. If the cul_reason
241            //  field does not exist this SQL would fail, however, cul_reason
242            //  not existing also means this change has been previously applied.
243            $updater->modifyExtensionField(
244                'cu_log',
245                'cul_actor',
246                "$base/$dbType/patch-cu_log-drop-actor_default.sql"
247            );
248        }
249        $updater->dropExtensionField(
250            'cu_log',
251            'cul_reason',
252            "$base/$dbType/patch-cu_log-drop-cul_reason.sql"
253        );
254        $updater->modifyExtensionField(
255            'cu_log',
256            'cul_reason_id',
257            "$base/$dbType/patch-cu_log-drop-cul_reason_id_default.sql"
258        );
259        $updater->dropExtensionField(
260            'cu_changes',
261            'cuc_user',
262            "$base/$dbType/patch-cu_changes-drop-cuc_user.sql"
263        );
264        $updater->addExtensionField(
265            'cu_changes',
266            'cuc_only_for_read_old',
267            "$base/$dbType/patch-cu_changes-add-cuc_only_for_read_old.sql"
268        );
269        $updater->dropExtensionField(
270            'cu_changes',
271            'cuc_comment',
272            "$base/$dbType/patch-cu_changes-drop-cuc_comment.sql"
273        );
274        $updater->modifyExtensionField(
275            'cu_changes',
276            'cuc_actor',
277            "$base/$dbType/patch-cu_changes-drop-defaults.sql"
278        );
279
280        // 1.41
281        $updater->addExtensionTable( 'cu_useragent_clienthints', "$base/$dbType/cu_useragent_clienthints.sql" );
282        $updater->addExtensionTable( 'cu_useragent_clienthints_map', "$base/$dbType/cu_useragent_clienthints_map.sql" );
283        $updater->addPostDatabaseUpdateMaintenance( MoveLogEntriesFromCuChanges::class );
284
285        // 1.42
286        $updater->addExtensionField(
287            'cu_log',
288            'cul_result_id',
289            "$base/$dbType/patch-cu_log-add-cul_result_id.sql"
290        );
291        if ( $dbType !== 'sqlite' ) {
292            $updater->modifyExtensionField(
293                'cu_changes',
294                'cuc_id',
295                "$base/$dbType/patch-cu_changes-modify-cuc_id-bigint.sql"
296            );
297        }
298        $updater->addPostDatabaseUpdateMaintenance( FixTrailingSpacesInLogs::class );
299        // If any columns are modified or removed from cu_private_event in the future, then make sure to only apply this
300        // patch if the later schema change has not yet been applied. Otherwise wikis using SQLite will have a DB error.
301        $updater->modifyExtensionField(
302            'cu_private_event',
303            'cupe_actor',
304            "$base/$dbType/patch-cu_private_event-modify-cupe_actor-nullable.sql"
305        );
306        $updater->addExtensionTable( 'cu_useragent', "$base/$dbType/cu_useragent.sql" );
307        $updater->addExtensionField(
308            'cu_changes',
309            'cuc_agent_id',
310            "$base/$dbType/patch-cu_changes-add-cuc_agent_id.sql"
311        );
312        $updater->addExtensionField(
313            'cu_log_event',
314            'cule_agent_id',
315            "$base/$dbType/patch-cu_log_event-add-cule_agent_id.sql"
316        );
317        $updater->addExtensionField(
318            'cu_private_event',
319            'cupe_agent_id',
320            "$base/$dbType/patch-cu_private_event-add-cupe_agent_id.sql"
321        );
322
323        if ( !$isCUInstalled ) {
324            // First time so populate cu_changes with recentchanges data.
325            // Note: We cannot completely rely on updatelog here for old entries
326            // as populateCheckUserTable.php doesn't check for duplicates
327            $updater->addPostDatabaseUpdateMaintenance( PopulateCheckUserTable::class );
328        }
329    }
330}