diff --git a/web/clitools/searchdupes.php b/web/clitools/searchdupes.php new file mode 100755 index 0000000..5b5fc80 --- /dev/null +++ b/web/clitools/searchdupes.php @@ -0,0 +1,195 @@ +#!/usr/bin/php +. +*/ + +define('N',"\n"); +define('SNAME',basename(__FILE__)); +define('CONFIGFP',__DIR__.'/../../conf/mustard.ini'); + +$help='SYNOPSYS + '.SNAME.' [options] +DESCRIPTION + This is a script to check the «Instances» table of mastostart database for + multiple records with the same URI value. +OPTIONS + -d, --delete + For each URI with multiple records, give the possibility to interactively + choose which record to keep, and delete the others (default); or to + automatically keep the record with the lowest ID and delete the others + (see below). + -I, --nonint + Disable interactive mode. + -h, --help + Show this help text and exit.'.N; + +$opts=[ + 'delete'=>false, + 'interactive'=>true +]; + +for ($i=1; $i<$argc; $i++) { + if ($argv[$i]=='-h' || $argv[$i]=='--help') { + mexit($help,0); + } elseif ($argv[$i]=='-d' || $argv[$i]=='--delete') { + $opts['delete']=true; + } elseif ($argv[$i]=='-I' || $argv[$i]=='--nonint') { + $opts['interactive']=false; + } else { + mexit('Don’t know how to interpret «'.$argv[$i].'», please read the help text using «-h» or «--help» option.'.N,1); + } +} + +use function mysqli_real_escape_string as myesc; + +function mexit($msg,$code) { + global $link; + if (isset($link)) mysqli_close($link); + if ($code>0) + fwrite(STDERR,$msg); + else + echo($msg); + exit($code); +} + +$iniarr=@parse_ini_file(CONFIGFP) + or mexit('Could not open config file «'.CONFIGFP.'».'.N,1); + +try { $link=mysqli_connect($iniarr['db_host'],$iniarr['db_admin_name'],$iniarr['db_admin_password'],$iniarr['db_name'],$iniarr['db_port'],$iniarr['db_socket']); } +catch (Exception $error) { mexit('Could not connect to mysql server: '.$error->getMessage().' (error code: '.$error->getCode().').'.N,1); } +mysqli_set_charset($link,'utf8mb4'); + +function myq(&$l,$q) { + try { + $res=mysqli_query($l,$q); + } + catch (Exception $e) { + echo('query «'.$q.'» failed: '.$e->getMessage().' (error code: '.$e->getCode().').'.N); + exit(3); + } + return($res); +} + +$insts=[]; +$res=mysqli_query($link,'SELECT * FROM Instances'); +while ($row=mysqli_fetch_assoc($res)) + $insts[$row['URI']][]=$row; + +//ID: 83; FirstSeen: 1602617889; IsMastodon: 0; Dead: 0; New: 0; Good: 0; Chosen: 0; Priority: NULL; Visible: 0; Noxious: 0; NoxReason: NULL; NoxLastModTS: NULL; URI: ambrosia.cafe; Title: ambrosia café; ShortDesc: NULL; LongDesc: Pleroma: An efficient and flexible fediverse server; OurDesc: NULL; OurDescEN: NULL; LocalityID: NULL; OurLangsLock: 0; Email: admin@ambrosia.cafe; Software: NULL; Version: 2.7.2 (compatible; Pleroma 2.4.2); UserCount: 3; StatusCount: 299; DomainCount: 2900; ActiveUsersMonth: NULL; ActiveUsersHalfYear: NULL; Thumb: https://ambrosia.cafe/instance/thumbnail.jpeg; RegOpen: 0; RegReqApproval: 0; MaxTootChars: 5000; AdmAccount: NULL; AdmDisplayName: NULL; AdmCreatedAt: NULL; AdmNote: NULL; AdmURL: NULL; AdmAvatar: NULL; AdmHeader: NULL; LastCheckOk: 0; GuestID: NULL; LastGuestEdit: NULL; InsertTS: NULL; RPos: 4291; + +$tot=0; +foreach ($insts as $uri=>$rows) + if (count($rows)>1) $tot++; + +if ($tot>0 && $opts['delete']) { + $i=0; + foreach ($insts as $uri=>$rows) { + $cr=count($rows); + if ($cr>1) { + $i++; + echo('<<< '.$i.'/'.$tot.': '.$uri.' ('.$cr.' records) >>>'.N.N); + $buff=[]; + foreach ($rows as $row) { + $row['FirstSeen']=utstd($row['FirstSeen']); + $row['NoxLastModTS']=utstd($row['NoxLastModTS']); + $row['AdmCreatedAt']=utstd($row['AdmCreatedAt']); + $row['LastGuestEdit']=utstd($row['LastGuestEdit']); + $row['InsertTS']=utstd($row['InsertTS']); + $out=''; + foreach ($row as $key=>$val) + $out.=$key.': '.pr($val).'; '; + $out=substr($out,0,-2); + //echo($out.N.N); + $buff[$row['ID']]=['id'=>$row['ID'],'row'=>$out]; + } + asort($buff); + $ii=0; + $recs=[]; + foreach ($buff as $rec) { + $ii++; + $recs[$ii]=$rec; + echo($ii.': '.$rec['row'].N.N); + } + if ($opts['interactive']) { + $ans=[]; + for ($iii=1; $iii<=$ii; $iii++) + $ans[]=$iii; + $ans[]='e'; + $inp=null; + while (!in_array($inp,$ans)) { + echo('Which one do you want to keep? ['.implode('/',$ans).'] '); + $inp=strtolower(trim(fgets(STDIN))); + } + } else { + $inp=1; + } + if ($inp=='e') { + mexit('Ok, bye :-)'.N,0); + } else { + $inp+=0; + echo(N.'Ok, i’ll keep the record with ID='.$recs[$inp]['id'].N); + foreach ($recs as $key=>$rec) { + if ($key!=$inp) { + echo('Deleting record with ID='.$rec['id'].' ...'.N); + myq($link,'DELETE FROM Instances WHERE ID='.$rec['id']); + echo('Deleted '.mysqli_affected_rows($link).' records from Instances table.'.N); + myq($link,'DELETE FROM InstActivity WHERE InstID='.$rec['id']); + echo('Deleted '.mysqli_affected_rows($link).' records from InstActivity table.'.N); + myq($link,'DELETE FROM InstChecks WHERE InstID='.$rec['id']); + echo('Deleted '.mysqli_affected_rows($link).' records from InstChecks table.'.N); + myq($link,'DELETE FROM InstFinancing WHERE InstID='.$rec['id']); + echo('Deleted '.mysqli_affected_rows($link).' records from InstFinancing table.'.N); + myq($link,'DELETE FROM InstLangs WHERE InstID='.$rec['id']); + echo('Deleted '.mysqli_affected_rows($link).' records from InstLangs table.'.N); + myq($link,'DELETE FROM InstOurLangs WHERE InstID='.$rec['id']); + echo('Deleted '.mysqli_affected_rows($link).' records from InstOurLangs table.'.N); + myq($link,'DELETE FROM InstPolicies WHERE InstID='.$rec['id']); + echo('Deleted '.mysqli_affected_rows($link).' records from InstPolicies table.'.N); + myq($link,'DELETE FROM InstTags WHERE InstID='.$rec['id']); + echo('Deleted '.mysqli_affected_rows($link).' records from InstTags table.'.N); + myq($link,'DELETE FROM InstTrends WHERE InstID='.$rec['id']); + echo('Deleted '.mysqli_affected_rows($link).' records from InstTrends table.'.N); + $users=myq($link,'SELECT ID FROM Users WHERE InstID='.$rec['id']); + $iii=0; + while ($user=mysqli_fetch_assoc($users)) { + myq($link,'DELETE FROM UsersFields WHERE UserID='.$user['ID']); + $iii+=mysqli_affected_rows($link); + } + echo('Deleted '.$iii.' records from UsersFields table.'.N); + myq($link,'DELETE FROM Users WHERE InstID='.$rec['id']); + echo('Deleted '.mysqli_affected_rows($link).' records from Users table.'.N); + } + } + echo(N); + } + } + } +} +echo('Total dupes: '.$tot.N); +mysqli_close($link); +exit(0); + +function utstd($val) { + if (is_null($val)) return(null); + $val=round($val); + return(date('Y-m-d H:i:s',$val)); +} +function pr($val) { + if (is_null($val)) return('NULL'); + return($val); +} + +?>