26 require_once __DIR__ .
'/Maintenance.php';
43 parent::__construct();
48 "Only rebuild rows in requested time range (in YYYYMMDDHHMMSS format)",
54 "Only rebuild rows in requested time range (in YYYYMMDDHHMMSS format)",
66 $this->
fatalError(
"Both 'from' and 'to' must be given, or neither" );
69 $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
78 $this->
output(
"Done.\n" );
93 $days = $sec / 24 / 3600;
94 $this->
output(
"Rebuilding range of $sec seconds ($days days)\n" );
99 $this->
output(
"Rebuilding \$wgRCMaxAge=$wgRCMaxAge seconds ($days days)\n" );
102 $this->cutoffTo = time();
105 $this->
output(
"Clearing recentchanges table for time range...\n" );
106 $rcids = $dbw->selectFieldValues(
110 'rc_timestamp > ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffFrom ) ),
111 'rc_timestamp < ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffTo ) )
114 foreach ( array_chunk( $rcids, $this->
getBatchSize() )
as $rcidBatch ) {
115 $dbw->delete(
'recentchanges', [
'rc_id' => $rcidBatch ], __METHOD__ );
119 $this->
output(
"Loading from page and revision tables...\n" );
121 $commentQuery = $commentStore->getJoin(
'rev_comment' );
124 [
'revision',
'page' ] + $commentQuery[
'tables'] + $actorQuery[
'tables'],
134 ] + $commentQuery[
'fields'] + $actorQuery[
'fields'],
136 'rev_timestamp > ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffFrom ) ),
137 'rev_timestamp < ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffTo ) )
140 [
'ORDER BY' =>
'rev_timestamp DESC' ],
142 'page' => [
'JOIN',
'rev_page=page_id' ],
143 ] + $commentQuery[
'joins'] + $actorQuery[
'joins']
146 $this->
output(
"Inserting from page and revision tables...\n" );
149 foreach (
$res as $row ) {
150 $comment = $commentStore->getComment(
'rev_comment', $row );
155 'rc_timestamp' => $row->rev_timestamp,
156 'rc_namespace' => $row->page_namespace,
157 'rc_title' => $row->page_title,
158 'rc_minor' => $row->rev_minor_edit,
160 'rc_new' => $row->page_is_new,
161 'rc_cur_id' => $row->page_id,
162 'rc_this_oldid' => $row->rev_id,
163 'rc_last_oldid' => 0,
166 'rc_deleted' => $row->rev_deleted
167 ] + $commentStore->insert( $dbw,
'rc_comment', $comment )
168 + $actorMigration->getInsertValues( $dbw,
'rc_user',
$user ),
184 $this->
output(
"Updating links and size differences...\n" );
186 # Fill in the rc_last_oldid field, which points to the previous edit
189 [
'rc_cur_id',
'rc_this_oldid',
'rc_timestamp' ],
191 "rc_timestamp > " . $dbw->addQuotes( $dbw->timestamp( $this->cutoffFrom ) ),
192 "rc_timestamp < " . $dbw->addQuotes( $dbw->timestamp( $this->cutoffTo ) )
195 [
'ORDER BY' =>
'rc_cur_id,rc_timestamp' ]
202 foreach (
$res as $obj ) {
205 if ( $obj->rc_cur_id != $lastCurId ) {
206 # Switch! Look up the previous last edit, if any
207 $lastCurId = intval( $obj->rc_cur_id );
208 $emit = $obj->rc_timestamp;
210 $row = $dbw->selectRow(
212 [
'rev_id',
'rev_len' ],
213 [
'rev_page' => $lastCurId,
"rev_timestamp < " . $dbw->addQuotes( $emit ) ],
215 [
'ORDER BY' =>
'rev_timestamp DESC' ]
218 $lastOldId = intval( $row->rev_id );
219 # Grab the last text size if available
220 $lastSize = !is_null( $row->rev_len ) ? intval( $row->rev_len ) :
null;
229 if ( $lastCurId == 0 ) {
230 $this->
output(
"Uhhh, something wrong? No curid\n" );
232 # Grab the entry's text size
233 $size = (int)$dbw->selectField(
236 [
'rev_id' => $obj->rc_this_oldid ],
243 'rc_last_oldid' => $lastOldId,
247 'rc_old_len' => $lastSize,
248 'rc_new_len' => $size,
251 'rc_cur_id' => $lastCurId,
252 'rc_this_oldid' => $obj->rc_this_oldid,
253 'rc_timestamp' => $obj->rc_timestamp
258 $lastOldId = intval( $obj->rc_this_oldid );
280 $this->
output(
"Loading from user and logging tables...\n" );
282 $commentQuery = $commentStore->getJoin(
'log_comment' );
285 [
'logging' ] + $commentQuery[
'tables'] + $actorQuery[
'tables'],
296 ] + $commentQuery[
'fields'] + $actorQuery[
'fields'],
298 'log_timestamp > ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffFrom ) ),
299 'log_timestamp < ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffTo ) ),
304 [
'ORDER BY' =>
'log_timestamp DESC' ],
305 $commentQuery[
'joins'] + $actorQuery[
'joins']
308 $field = $dbw->fieldInfo(
'recentchanges',
'rc_cur_id' );
312 foreach (
$res as $row ) {
313 $comment = $commentStore->getComment(
'log_comment', $row );
318 'rc_timestamp' => $row->log_timestamp,
319 'rc_namespace' => $row->log_namespace,
320 'rc_title' => $row->log_title,
325 'rc_this_oldid' => 0,
326 'rc_last_oldid' => 0,
329 'rc_cur_id' => $field->isNullable()
331 : (int)$row->log_page,
332 'rc_log_type' => $row->log_type,
333 'rc_log_action' => $row->log_action,
334 'rc_logid' => $row->log_id,
335 'rc_params' => $row->log_params,
336 'rc_deleted' => $row->log_deleted
337 ] + $commentStore->insert( $dbw,
'rc_comment', $comment )
338 + $actorMigration->getInsertValues( $dbw,
'rc_user',
$user ),
358 # @FIXME: recognize other bot account groups (not the same as users with 'bot' rights)
359 # @NOTE: users with 'bot' rights choose when edits are bot edits or not. That information
360 # may be lost at this point (aside from joining on the patrol log table entries).
361 $botgroups = [
'bot' ];
364 # Flag our recent bot edits
366 $this->
output(
"Flagging bot account edits...\n" );
368 # Find all users that are bots
370 array_merge( [
'user_groups' ], $userQuery[
'tables'] ),
371 $userQuery[
'fields'],
372 [
'ug_group' => $botgroups ],
375 [
'user_group' => [
'JOIN',
'user_id = ug_user' ] ] + $userQuery[
'joins']
379 foreach (
$res as $obj ) {
383 # Fill in the rc_bot field
387 foreach ( $actorQuery[
'orconds']
as $cond ) {
388 $rcids = array_merge( $rcids, $dbw->selectFieldValues(
389 [
'recentchanges' ] + $actorQuery[
'tables'],
392 "rc_timestamp > " . $dbw->addQuotes( $dbw->timestamp( $this->cutoffFrom ) ),
393 "rc_timestamp < " . $dbw->addQuotes( $dbw->timestamp( $this->cutoffTo ) ),
401 $rcids = array_values( array_unique( $rcids ) );
403 foreach ( array_chunk( $rcids, $this->
getBatchSize() )
as $rcidBatch ) {
407 [
'rc_id' => $rcidBatch ],
415 # Flag our recent autopatrolled edits
419 $this->
output(
"Flagging auto-patrolled edits...\n" );
421 # Find all users in RC with autopatrol rights
423 array_merge( [
'user_groups' ], $userQuery[
'tables'] ),
424 $userQuery[
'fields'],
425 [
'ug_group' => $autopatrolgroups ],
428 [
'user_group' => [
'JOIN',
'user_id = ug_user' ] ] + $userQuery[
'joins']
431 foreach (
$res as $obj ) {
435 # Fill in the rc_patrolled field
436 if ( $patrolusers ) {
438 foreach ( $actorQuery[
'orconds']
as $cond ) {
441 [
'rc_patrolled' => 1 ],
444 'rc_timestamp > ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffFrom ) ),
445 'rc_timestamp < ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffTo ) ),
462 $this->
output(
"Removing duplicate revision and logging entries...\n" );
465 [
'logging',
'log_search' ],
466 [
'ls_value',
'ls_log_id' ],
468 'ls_log_id = log_id',
469 'ls_field' =>
'associated_rev_id',
470 'log_type' =>
'upload',
471 'log_timestamp > ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffFrom ) ),
472 'log_timestamp < ' . $dbw->addQuotes( $dbw->timestamp( $this->cutoffTo ) ),
478 foreach (
$res as $obj ) {
479 $rev_id = $obj->ls_value;
480 $log_id = $obj->ls_log_id;
485 [
'rc_this_oldid' => $rev_id ],
486 [
'rc_logid' => $log_id ],
493 [
'rc_this_oldid' => $rev_id,
'rc_logid' => 0 ],
509 $this->
output(
"Deleting feed timestamps.\n" );
511 $wanCache = MediaWikiServices::getInstance()->getMainWANObjectCache();
513 $wanCache->delete( $wanCache->makeKey(
'rcfeed', $feed,
'timestamp' ) ); # Good enough
for now.