Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 59 |
|
0.00% |
0 / 3 |
CRAP | |
0.00% |
0 / 1 |
FixTrailingWhitespaceIds | |
0.00% |
0 / 53 |
|
0.00% |
0 / 3 |
72 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
doDBUpdates | |
0.00% |
0 / 48 |
|
0.00% |
0 / 1 |
42 | |||
getUpdateKey | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\DiscussionTools\Maintenance; |
4 | |
5 | use MediaWiki\Maintenance\LoggedUpdateMaintenance; |
6 | use Wikimedia\Rdbms\DBQueryError; |
7 | use Wikimedia\Rdbms\IExpression; |
8 | use Wikimedia\Rdbms\LikeValue; |
9 | |
10 | $IP = getenv( 'MW_INSTALL_PATH' ); |
11 | if ( $IP === false ) { |
12 | $IP = __DIR__ . '/../../..'; |
13 | } |
14 | require_once "$IP/maintenance/Maintenance.php"; |
15 | |
16 | class FixTrailingWhitespaceIds extends LoggedUpdateMaintenance { |
17 | |
18 | public function __construct() { |
19 | parent::__construct(); |
20 | $this->addDescription( 'Fix comment IDs with trailing whitespace' ); |
21 | $this->setBatchSize( 1000 ); |
22 | $this->requireExtension( 'DiscussionTools' ); |
23 | } |
24 | |
25 | /** |
26 | * @inheritDoc |
27 | */ |
28 | public function doDBUpdates() { |
29 | $dbw = $this->getDB( DB_PRIMARY ); |
30 | |
31 | $this->output( "Fixing DiscussionTools IDs with trailing whitespace..\n" ); |
32 | $total = 0; |
33 | |
34 | $skippedIds = []; |
35 | |
36 | do { |
37 | // Match things that are possibly heading IDs with trailing underscores, |
38 | // possibly followed by a timestamp. |
39 | // As we are using LIKE there's a small chance of false positives, but |
40 | // this will become no-ops as we use a stricter RegExp later. |
41 | |
42 | // Trailing underscore |
43 | // _-%\_ |
44 | $like1 = new LikeValue( $dbw->anyChar(), '-', $dbw->anyString(), '_' ); |
45 | // Trailing underscore followed by short timestamp |
46 | // _-%\_-2%00 |
47 | $like2 = new LikeValue( $dbw->anyChar(), '-', $dbw->anyString(), '_-2', $dbw->anyString(), '00' ); |
48 | // Trailing underscore followed by long timestamp |
49 | // _-%\_-2%00.000Z |
50 | $like3 = new LikeValue( $dbw->anyChar(), '-', $dbw->anyString(), '_-2', $dbw->anyString(), '00.000Z' ); |
51 | |
52 | $itemIdQueryBuilder = $dbw->newSelectQueryBuilder() |
53 | ->from( 'discussiontools_item_ids' ) |
54 | ->field( 'itid_itemid' ) |
55 | ->where( |
56 | $dbw->orExpr( [ |
57 | $dbw->expr( 'itid_itemid', IExpression::LIKE, $like1 ), |
58 | $dbw->expr( 'itid_itemid', IExpression::LIKE, $like2 ), |
59 | $dbw->expr( 'itid_itemid', IExpression::LIKE, $like3 ), |
60 | ] ) |
61 | ) |
62 | ->caller( __METHOD__ ) |
63 | ->limit( $this->getBatchSize() ); |
64 | |
65 | if ( $skippedIds ) { |
66 | $itemIdQueryBuilder->where( $dbw->expr( 'itid_itemid', '!=', $skippedIds ) ); |
67 | } |
68 | |
69 | $itemIds = $itemIdQueryBuilder->fetchFieldValues(); |
70 | |
71 | if ( !$itemIds ) { |
72 | break; |
73 | } |
74 | |
75 | foreach ( $itemIds as $itemId ) { |
76 | $fixedItemId = preg_replace( |
77 | '/^([hc]\-.*)_(\-([0-9]{14}|[0-9-]{10}T[0-9:]{6}00.000Z))?$/', |
78 | '$1$2', |
79 | $itemId |
80 | ); |
81 | if ( $fixedItemId === $itemId ) { |
82 | // In the rare case we got a false positive from the LIKE, add this to a list of skipped IDs |
83 | // so we don't keep selecting it, and end up in an infinite loop |
84 | $skippedIds[] = $itemId; |
85 | continue; |
86 | } |
87 | try { |
88 | $dbw->newUpdateQueryBuilder() |
89 | ->update( 'discussiontools_item_ids' ) |
90 | ->set( [ 'itid_itemid' => $fixedItemId ] ) |
91 | ->where( [ 'itid_itemid' => $itemId ] ) |
92 | ->caller( __METHOD__ )->execute(); |
93 | } catch ( DBQueryError $err ) { |
94 | // Give up on updating in case of complex conflicts (T356196#9913698) |
95 | $this->output( "Failed to update $itemId\n" ); |
96 | $skippedIds[] = $itemId; |
97 | continue; |
98 | } |
99 | |
100 | $total += $dbw->affectedRows(); |
101 | } |
102 | |
103 | $this->waitForReplication(); |
104 | $this->output( "$total\n" ); |
105 | } while ( true ); |
106 | |
107 | $this->output( "Fixing DiscussionTools IDs with trailing whitespace: done.\n" ); |
108 | |
109 | return true; |
110 | } |
111 | |
112 | /** |
113 | * @inheritDoc |
114 | */ |
115 | public function getUpdateKey() { |
116 | return 'DiscussionToolsFixTrailingWhitespaceIds'; |
117 | } |
118 | } |
119 | |
120 | $maintClass = FixTrailingWhitespaceIds::class; |
121 | require_once RUN_MAINTENANCE_IF_MAIN; |