52 $this->db = $database;
60 private function out( $str ) {
61 call_user_func( $this->outputCallback, $str );
70 $info = $this->db->indexInfo(
'user',
'user_name', __METHOD__ );
72 $this->
out(
"WARNING: doesn't seem to have user_name index at all!\n" );
77 # Confusingly, 'Non_unique' is 0 for *unique* indexes,
78 # and 1 for *non-unique* indexes. Pass the crack, MySQL,
79 # it's obviously some good stuff!
80 return ( $info[0]->Non_unique == 0 );
115 echo
"$dbDomain already has a unique index on its user table.\n";
122 $this->
out(
"Checking for duplicate accounts...\n" );
124 $count = count( $dupes );
126 $this->
out(
"Found $count accounts with duplicate records on $dbDomain.\n" );
128 $this->reassigned = 0;
130 foreach ( $dupes as $name ) {
131 $this->
examine( $name, $doDelete );
138 if ( $this->reassigned > 0 ) {
140 $this->
out(
"$this->reassigned duplicate accounts had edits "
141 .
"reassigned to a canonical record id.\n" );
143 $this->
out(
"$this->reassigned duplicate accounts need to have edits reassigned.\n" );
147 if ( $this->trimmed > 0 ) {
150 "$this->trimmed duplicate user records were deleted from $dbDomain.\n" );
153 "$this->trimmed duplicate user accounts were found on $dbDomain " .
154 "which can be removed safely.\n"
159 if ( $this->failed > 0 ) {
160 $this->
out(
"Something terribly awry; $this->failed duplicate accounts were not removed.\n" );
165 if ( $this->trimmed == 0 || $doDelete ) {
166 $this->
out(
"It is now safe to apply the unique index on user_name.\n" );
170 $this->
out(
"Run this script again with the --fix option to automatically delete them.\n" );
180 $set = [
'user',
'revision' ];
181 $names = array_map( [ $this,
'lockTable' ], $set );
182 $tables = implode(
',', $names );
184 $this->db->query(
"LOCK TABLES $tables", __METHOD__ );
188 return $this->db->tableName( $table ) .
' WRITE';
195 $this->db->query(
"UNLOCK TABLES", __METHOD__ );
203 $user = $this->db->tableName(
'user' );
204 $result = $this->db->query(
205 "SELECT user_name,COUNT(*) AS n
208 HAVING n > 1", __METHOD__ );
211 foreach ( $result as $row ) {
212 $list[] = $row->user_name;
225 private function examine( $name, $doDelete ) {
226 $result = $this->db->select(
'user',
228 [
'user_name' => $name ],
231 $firstRow = $this->db->fetchObject( $result );
232 $firstId = $firstRow->user_id;
233 $this->
out(
"Record that will be used for '$name' is user_id=$firstId\n" );
235 foreach ( $result as $row ) {
236 $dupeId = $row->user_id;
237 $this->
out(
"... dupe id $dupeId: " );
241 $this->
out(
"has $edits edits! " );
245 if ( $newEdits == 0 ) {
246 $this->
out(
"confirmed cleaned. " );
249 $this->
out(
"WARNING! $newEdits remaining edits for $dupeId; NOT deleting user.\n" );
253 $this->
out(
"(will need to reassign edits on fix)" );
256 $this->
out(
"ok, no edits. " );
274 return intval( $this->db->selectField(
277 [
'rev_user' => $userid ],
286 $this->
out(
'reassigning... ' );
287 $this->db->update(
'revision',
288 [
'rev_user' => $to ],
289 [
'rev_user' => $from ],
291 $this->
out(
"ok. " );
299 $this->
out(
"deleting..." );
300 $this->db->delete(
'user', [
'user_id' => $userid ], __METHOD__ );