MediaWiki  master
migrateUserGroup.php
Go to the documentation of this file.
1 <?php
25 
26 require_once __DIR__ . '/Maintenance.php';
27 
34  public function __construct() {
35  parent::__construct();
36  $this->addDescription( 'Re-assign users from an old group to a new one' );
37  $this->addArg( 'oldgroup', 'Old user group key', true );
38  $this->addArg( 'newgroup', 'New user group key', true );
39  $this->setBatchSize( 200 );
40  }
41 
42  public function execute() {
43  $count = 0;
44  $oldGroup = $this->getArg( 0 );
45  $newGroup = $this->getArg( 1 );
46  $dbw = $this->getDB( DB_PRIMARY );
47  $batchSize = $this->getBatchSize();
48  $start = $dbw->newSelectQueryBuilder()
49  ->select( 'MIN(ug_user)' )
50  ->from( 'user_groups' )
51  ->where( [ 'ug_group' => $oldGroup ] )
52  ->caller( __FUNCTION__ )->fetchField();
53  $end = $dbw->newSelectQueryBuilder()
54  ->select( 'MAX(ug_user)' )
55  ->from( 'user_groups' )
56  ->where( [ 'ug_group' => $oldGroup ] )
57  ->caller( __FUNCTION__ )->fetchField();
58  if ( $start === null ) {
59  $this->fatalError( "Nothing to do - no users in the '$oldGroup' group" );
60  }
61  # Do remaining chunk
62  $end += $batchSize - 1;
63  $blockStart = $start;
64  $blockEnd = $start + $batchSize - 1;
65  // Migrate users over in batches...
66  while ( $blockEnd <= $end ) {
67  $affected = 0;
68  $this->output( "Doing users $blockStart to $blockEnd\n" );
69 
70  $this->beginTransaction( $dbw, __METHOD__ );
71  $dbw->update( 'user_groups',
72  [ 'ug_group' => $newGroup ],
73  [ 'ug_group' => $oldGroup,
74  "ug_user BETWEEN " . (int)$blockStart . " AND " . (int)$blockEnd ],
75  __METHOD__,
76  [ 'IGNORE' ]
77  );
78  $affected += $dbw->affectedRows();
79  // Delete rows that the UPDATE operation above had to ignore.
80  // This happens when a user is in both the old and new group.
81  // Updating the row for the old group membership failed since
82  // user/group is UNIQUE.
83  $dbw->delete( 'user_groups',
84  [ 'ug_group' => $oldGroup,
85  "ug_user BETWEEN " . (int)$blockStart . " AND " . (int)$blockEnd ],
86  __METHOD__
87  );
88  $affected += $dbw->affectedRows();
89  $this->commitTransaction( $dbw, __METHOD__ );
90 
91  // Clear cache for the affected users (T42340)
92  if ( $affected > 0 ) {
93  // XXX: This also invalidates cache of unaffected users that
94  // were in the new group and not in the group.
95  $res = $dbw->newSelectQueryBuilder()
96  ->select( 'ug_user' )
97  ->from( 'user_groups' )
98  ->where( [
99  'ug_group' => $newGroup,
100  "ug_user BETWEEN " . (int)$blockStart . " AND " . (int)$blockEnd
101  ] )
102  ->caller( __METHOD__ )->fetchResultSet();
103  if ( $res !== false ) {
104  foreach ( $res as $row ) {
105  $user = User::newFromId( $row->ug_user );
106  $user->invalidateCache();
107  }
108  }
109  }
110 
111  $count += $affected;
112  $blockStart += $batchSize;
113  $blockEnd += $batchSize;
114  }
115  $this->output( "Done! $count users in group '$oldGroup' are now in '$newGroup' instead.\n" );
116  }
117 }
118 
119 $maintClass = MigrateUserGroup::class;
120 require_once RUN_MAINTENANCE_IF_MAIN;
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
Definition: Maintenance.php:66
getDB( $db, $groups=[], $dbDomain=false)
Returns a database to be used by current maintenance script.
addArg( $arg, $description, $required=true, $multi=false)
Add some args that are needed.
beginTransaction(IDatabase $dbw, $fname)
Begin a transaction on a DB.
commitTransaction(IDatabase $dbw, $fname)
Commit the transaction on a DB handle and wait for replica DBs to catch up.
output( $out, $channel=null)
Throw some output to the user.
getBatchSize()
Returns batch size.
getArg( $argId=0, $default=null)
Get an argument.
addDescription( $text)
Set the description text.
setBatchSize( $s=0)
fatalError( $msg, $exitCode=1)
Output a message and terminate the current script.
internal since 1.36
Definition: User.php:98
Maintenance script that re-assigns users from an old group to a new one.
execute()
Do the actual work.
__construct()
Default constructor.
const DB_PRIMARY
Definition: defines.php:28