Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 51 |
|
0.00% |
0 / 2 |
CRAP | |
0.00% |
0 / 1 |
RenameUserJob | |
0.00% |
0 / 51 |
|
0.00% |
0 / 2 |
110 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
run | |
0.00% |
0 / 48 |
|
0.00% |
0 / 1 |
90 |
1 | <?php |
2 | |
3 | use MediaWiki\Config\Config; |
4 | use MediaWiki\MainConfigNames; |
5 | use MediaWiki\Title\Title; |
6 | use Wikimedia\Rdbms\ILBFactory; |
7 | |
8 | /** |
9 | * Custom job to perform updates on tables in busier environments |
10 | * |
11 | * Job parameters include: |
12 | * - table : DB table to update |
13 | * - column : The *_user_text column to update |
14 | * - oldname : The old user name |
15 | * - newname : The new user name |
16 | * - count : The expected number of rows to update in this batch |
17 | * |
18 | * Additionally, one of the following groups of parameters must be set: |
19 | * a) The timestamp based rename parameters: |
20 | * - timestampColumn : The *_timestamp column |
21 | * - minTimestamp : The minimum bound of the timestamp column range for this batch |
22 | * - maxTimestamp : The maximum bound of the timestamp column range for this batch |
23 | * - uniqueKey : A column that is unique (preferably the PRIMARY KEY) [optional] |
24 | * b) The unique key based rename parameters: |
25 | * - uniqueKey : A column that is unique (preferably the PRIMARY KEY) |
26 | * - keyId : A list of values for this column to determine rows to update for this batch |
27 | * |
28 | * To avoid some race conditions, the following parameters should be set: |
29 | * - userID : The ID of the user to update |
30 | * - uidColumn : The *_user_id column |
31 | */ |
32 | class RenameUserJob extends Job { |
33 | /** @var int */ |
34 | private $updateRowsPerQuery; |
35 | |
36 | /** @var ILBFactory */ |
37 | private $lbFactory; |
38 | |
39 | public function __construct( |
40 | Title $title, |
41 | $params, |
42 | Config $config, |
43 | ILBFactory $lbFactory |
44 | ) { |
45 | parent::__construct( 'renameUser', $title, $params ); |
46 | |
47 | $this->updateRowsPerQuery = $config->get( MainConfigNames::UpdateRowsPerQuery ); |
48 | $this->lbFactory = $lbFactory; |
49 | } |
50 | |
51 | public function run() { |
52 | $dbw = $this->lbFactory->getPrimaryDatabase(); |
53 | $table = $this->params['table']; |
54 | $column = $this->params['column']; |
55 | |
56 | $oldname = $this->params['oldname']; |
57 | $newname = $this->params['newname']; |
58 | if ( isset( $this->params['userID'] ) ) { |
59 | $userID = $this->params['userID']; |
60 | $uidColumn = $this->params['uidColumn']; |
61 | } else { |
62 | $userID = null; |
63 | $uidColumn = null; |
64 | } |
65 | if ( isset( $this->params['timestampColumn'] ) ) { |
66 | $timestampColumn = $this->params['timestampColumn']; |
67 | $minTimestamp = $this->params['minTimestamp']; |
68 | $maxTimestamp = $this->params['maxTimestamp']; |
69 | } else { |
70 | $timestampColumn = null; |
71 | $minTimestamp = null; |
72 | $maxTimestamp = null; |
73 | } |
74 | $uniqueKey = $this->params['uniqueKey'] ?? null; |
75 | $keyId = $this->params['keyId'] ?? null; |
76 | |
77 | # Conditions like "*_user_text = 'x' |
78 | $conds = [ $column => $oldname ]; |
79 | # If user ID given, add that to condition to avoid rename collisions |
80 | if ( $userID !== null ) { |
81 | $conds[$uidColumn] = $userID; |
82 | } |
83 | # Bound by timestamp if given |
84 | if ( $timestampColumn !== null ) { |
85 | $conds[] = $dbw->expr( $timestampColumn, '>=', $minTimestamp ); |
86 | $conds[] = $dbw->expr( $timestampColumn, '<=', $maxTimestamp ); |
87 | # Bound by unique key if given (B/C) |
88 | } elseif ( $uniqueKey !== null && $keyId !== null ) { |
89 | $conds[$uniqueKey] = $keyId; |
90 | } else { |
91 | throw new InvalidArgumentException( 'Expected ID batch or time range' ); |
92 | } |
93 | |
94 | # Actually update the rows for this job... |
95 | if ( $uniqueKey !== null ) { |
96 | // Select the rows to update by PRIMARY KEY |
97 | $ids = $dbw->newSelectQueryBuilder() |
98 | ->select( $uniqueKey ) |
99 | ->from( $table ) |
100 | ->where( $conds ) |
101 | ->caller( __METHOD__ )->fetchFieldValues(); |
102 | # Update these rows by PRIMARY KEY to avoid replica lag |
103 | foreach ( array_chunk( $ids, $this->updateRowsPerQuery ) as $batch ) { |
104 | $dbw->commit( __METHOD__, 'flush' ); |
105 | $this->lbFactory->waitForReplication(); |
106 | |
107 | $dbw->newUpdateQueryBuilder() |
108 | ->update( $table ) |
109 | ->set( [ $column => $newname ] ) |
110 | ->where( [ $column => $oldname, $uniqueKey => $batch ] ) |
111 | ->caller( __METHOD__ )->execute(); |
112 | } |
113 | } else { |
114 | # Update the chunk of rows directly |
115 | $dbw->newUpdateQueryBuilder() |
116 | ->update( $table ) |
117 | ->set( [ $column => $newname ] ) |
118 | ->where( $conds ) |
119 | ->caller( __METHOD__ )->execute(); |
120 | } |
121 | |
122 | return true; |
123 | } |
124 | } |