MediaWiki REL1_30
RenameUserJob.php
Go to the documentation of this file.
1<?php
2
28class RenameUserJob extends Job {
29 public function __construct( Title $title, $params = [], $id = 0 ) {
30 parent::__construct( 'renameUser', $title, $params, $id );
31 }
32
33 public function run() {
35
36 $table = $this->params['table'];
37 $column = $this->params['column'];
38 $oldname = $this->params['oldname'];
39 $newname = $this->params['newname'];
40 $count = $this->params['count'];
41 if ( isset( $this->params['userID'] ) ) {
42 $userID = $this->params['userID'];
43 $uidColumn = $this->params['uidColumn'];
44 } else {
45 $userID = null;
46 $uidColumn = null;
47 }
48 if ( isset( $this->params['timestampColumn'] ) ) {
49 $timestampColumn = $this->params['timestampColumn'];
50 $minTimestamp = $this->params['minTimestamp'];
51 $maxTimestamp = $this->params['maxTimestamp'];
52 } else {
53 $timestampColumn = null;
54 $minTimestamp = null;
55 $maxTimestamp = null;
56 }
57 $uniqueKey = isset( $this->params['uniqueKey'] ) ? $this->params['uniqueKey'] : null;
58 $keyId = isset( $this->params['keyId'] ) ? $this->params['keyId'] : null;
59 $logId = isset( $this->params['logId'] ) ? $this->params['logId'] : null;
60
61 $dbw = wfGetDB( DB_MASTER );
62 if ( $logId ) {
63 # Block until the transaction that inserted this job commits.
64 # The atomic section is for sanity as FOR UPDATE does not lock in auto-commit mode
65 # per http://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html.
66 $dbw->startAtomic( __METHOD__ );
67 $committed = $dbw->selectField( 'logging',
68 '1',
69 [ 'log_id' => $logId ],
70 __METHOD__,
71 [ 'FOR UPDATE' ]
72 );
73 $dbw->endAtomic( __METHOD__ );
74 # If the transaction inserting this job was rolled back, detect that
75 if ( $committed === false ) { // rollback happened?
76 throw new LogicException( 'Cannot run job if the account rename failed.' );
77 }
78 }
79
80 # Flush any state snapshot data (and release the lock above)
81 $dbw->commit( __METHOD__, 'flush' );
82
83 # Conditions like "*_user_text = 'x'
84 $conds = [ $column => $oldname ];
85 # If user ID given, add that to condition to avoid rename collisions
86 if ( $userID !== null ) {
87 $conds[$uidColumn] = $userID;
88 }
89 # Bound by timestamp if given
90 if ( $timestampColumn !== null ) {
91 $conds[] = "$timestampColumn >= " . $dbw->addQuotes( $minTimestamp );
92 $conds[] = "$timestampColumn <= " . $dbw->addQuotes( $maxTimestamp );
93 # Bound by unique key if given (B/C)
94 } elseif ( $uniqueKey !== null && $keyId !== null ) {
95 $conds[$uniqueKey] = $keyId;
96 } else {
97 throw new InvalidArgumentException( 'Expected ID batch or time range' );
98 }
99
100 $affectedCount = 0;
101 # Actually update the rows for this job...
102 if ( $uniqueKey !== null ) {
103 # Select the rows to update by PRIMARY KEY
104 $ids = $dbw->selectFieldValues( $table, $uniqueKey, $conds, __METHOD__ );
105 # Update these rows by PRIMARY KEY to avoid slave lag
106 foreach ( array_chunk( $ids, $wgUpdateRowsPerQuery ) as $batch ) {
107 $dbw->commit( __METHOD__, 'flush' );
108 wfWaitForSlaves();
109
110 $dbw->update( $table,
111 [ $column => $newname ],
112 [ $column => $oldname, $uniqueKey => $batch ],
113 __METHOD__
114 );
115 $affectedCount += $dbw->affectedRows();
116 }
117 } else {
118 # Update the chunk of rows directly
119 $dbw->update( $table,
120 [ $column => $newname ],
121 $conds,
122 __METHOD__
123 );
124 $affectedCount += $dbw->affectedRows();
125 }
126
127 # Special case: revisions may be deleted while renaming...
128 if ( $affectedCount < $count && $table === 'revision' && $timestampColumn !== null ) {
129 # If some revisions were not renamed, they may have been deleted.
130 # Do a pass on the archive table to get these straglers...
131 $ids = $dbw->selectFieldValues(
132 'archive',
133 'ar_id',
134 [
135 'ar_user_text' => $oldname,
136 'ar_user' => $userID,
137 // No user,rev_id index, so use timestamp to bound
138 // the rows. This can use the user,timestamp index.
139 "ar_timestamp >= '$minTimestamp'",
140 "ar_timestamp <= '$maxTimestamp'"
141 ],
142 __METHOD__
143 );
144 foreach ( array_chunk( $ids, $wgUpdateRowsPerQuery ) as $batch ) {
145 $dbw->commit( __METHOD__, 'flush' );
146 wfWaitForSlaves();
147
148 $dbw->update(
149 'archive',
150 [ 'ar_user_text' => $newname ],
151 [ 'ar_user_text' => $oldname, 'ar_id' => $batch ],
152 __METHOD__
153 );
154 }
155 }
156 # Special case: revisions may be restored while renaming...
157 if ( $affectedCount < $count && $table === 'archive' && $timestampColumn !== null ) {
158 # If some revisions were not renamed, they may have been restored.
159 # Do a pass on the revision table to get these straglers...
160 $ids = $dbw->selectFieldValues(
161 'revision',
162 'rev_id',
163 [
164 'rev_user_text' => $oldname,
165 'rev_user' => $userID,
166 // No user,rev_id index, so use timestamp to bound
167 // the rows. This can use the user,timestamp index.
168 "rev_timestamp >= '$minTimestamp'",
169 "rev_timestamp <= '$maxTimestamp'"
170 ],
171 __METHOD__
172 );
173 foreach ( array_chunk( $ids, $wgUpdateRowsPerQuery ) as $batch ) {
174 $dbw->commit( __METHOD__, 'flush' );
175 wfWaitForSlaves();
176
177 $dbw->update(
178 'revision',
179 [ 'rev_user_text' => $newname ],
180 [ 'rev_user_text' => $oldname, 'rev_id' => $batch ],
181 __METHOD__
182 );
183 }
184 }
185
186 return true;
187 }
188}
$wgUpdateRowsPerQuery
Number of rows to update per query.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Class to both describe a background job and handle jobs.
Definition Job.php:31
array $params
Array of job parameters.
Definition Job.php:36
Custom job to perform updates on tables in busier environments.
__construct(Title $title, $params=[], $id=0)
run()
Run the job.
Represents a title within MediaWiki.
Definition Title.php:39
const DB_MASTER
Definition defines.php:26