Merge pull request #7582 from annando/contact-dba

Avoid and remove duplicated contact entries
This commit is contained in:
Hypolite Petovan 2019-08-30 03:36:40 -04:00 committed by GitHub
commit 0c876db30f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 76 additions and 13 deletions

View file

@ -37,6 +37,9 @@ class PostUpdate
if (!self::update1297()) { if (!self::update1297()) {
return false; return false;
} }
if (!self::update1322()) {
return false;
}
return true; return true;
} }
@ -414,6 +417,40 @@ class PostUpdate
Logger::info('Done'); Logger::info('Done');
return true;
}
/**
* Remove contact duplicates
*
* @return bool "true" when the job is done
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
private static function update1322()
{
// Was the script completed?
if (Config::get('system', 'post_update_version') >= 1322) {
return true;
}
Logger::info('Start');
$contacts = DBA::p("SELECT `nurl`, `uid` FROM `contact`
WHERE EXISTS (SELECT `nurl` FROM `contact` AS `c2`
WHERE `c2`.`nurl` = `contact`.`nurl` AND `c2`.`id` != `contact`.`id` AND `c2`.`uid` = `contact`.`uid` AND `c2`.`network` IN (?, ?, ?) AND NOT `deleted`)
AND (`network` IN (?, ?, ?) OR (`uid` = ?)) AND NOT `deleted` GROUP BY `nurl`, `uid`",
Protocol::DIASPORA, Protocol::OSTATUS, Protocol::ACTIVITYPUB,
Protocol::DIASPORA, Protocol::OSTATUS, Protocol::ACTIVITYPUB, 0);
while ($contact = DBA::fetch($contacts)) {
Logger::info('Remove duplicates', ['nurl' => $contact['nurl'], 'uid' => $contact['uid']]);
Contact::removeDuplicates($contact['nurl'], $contact['uid']);
}
DBA::close($contact);
Config::set('system', 'post_update_version', 1322);
Logger::info('Done');
return true; return true;
} }
} }

View file

@ -136,6 +136,31 @@ class Contact extends BaseObject
return $contact; return $contact;
} }
/**
* Insert a row into the contact table
* Important: You can't use DBA::lastInsertId() after this call since it will be set to 0.
*
* @param array $fields field array
* @param bool $on_duplicate_update Do an update on a duplicate entry
*
* @return boolean was the insert successful?
* @throws \Exception
*/
public static function insert(array $fields, bool $on_duplicate_update = false)
{
$ret = DBA::insert('contact', $fields, $on_duplicate_update);
$contact = DBA::selectFirst('contact', ['nurl', 'uid'], ['id' => DBA::lastInsertId()]);
if (!DBA::isResult($contact)) {
// Shouldn't happen
return $ret;
}
// Search for duplicated contacts and get rid of them
self::removeDuplicates($contact['nurl'], $contact['uid']);
return $ret;
}
/** /**
* @param integer $id Contact ID * @param integer $id Contact ID
* @param array $fields Array of selected fields, empty for all * @param array $fields Array of selected fields, empty for all
@ -878,6 +903,13 @@ class Contact extends BaseObject
*/ */
public static function unmarkForArchival(array $contact) public static function unmarkForArchival(array $contact)
{ {
// Always unarchive the relay contact entry
if (!empty($contact['batch'])) {
$fields = ['term-date' => DBA::NULL_DATETIME, 'archive' => false];
$condition = ['batch' => $contact['batch'], 'contact-type' => self::TYPE_RELAY];
DBA::update('contact', $fields, $condition);
}
$condition = ['`id` = ? AND (`term-date` > ? OR `archive`)', $contact['id'], DBA::NULL_DATETIME]; $condition = ['`id` = ? AND (`term-date` > ? OR `archive`)', $contact['id'], DBA::NULL_DATETIME];
$exists = DBA::exists('contact', $condition); $exists = DBA::exists('contact', $condition);
@ -901,11 +933,6 @@ class Contact extends BaseObject
DBA::update('contact', $fields, ['id' => $contact['id']]); DBA::update('contact', $fields, ['id' => $contact['id']]);
DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($contact['url']), 'self' => false]); DBA::update('contact', $fields, ['nurl' => Strings::normaliseLink($contact['url']), 'self' => false]);
GContact::updateFromPublicContactURL($contact['url']); GContact::updateFromPublicContactURL($contact['url']);
if (!empty($contact['batch'])) {
$condition = ['batch' => $contact['batch'], 'contact-type' => self::TYPE_RELAY];
DBA::update('contact', $fields, $condition);
}
} }
/** /**
@ -1488,7 +1515,7 @@ class Contact extends BaseObject
if (!DBA::isResult($contact)) { if (!DBA::isResult($contact)) {
Logger::info('Create new contact', $fields); Logger::info('Create new contact', $fields);
DBA::insert('contact', $fields); self::insert($fields);
// We intentionally aren't using lastInsertId here. There is a chance for duplicates. // We intentionally aren't using lastInsertId here. There is a chance for duplicates.
$contact = DBA::selectFirst('contact', ['id'], $condition, ['order' => ['id']]); $contact = DBA::selectFirst('contact', ['id'], $condition, ['order' => ['id']]);
@ -1853,7 +1880,7 @@ class Contact extends BaseObject
} }
// Search for duplicated contacts and get rid of them // Search for duplicated contacts and get rid of them
if (self::handleDuplicates(Strings::normaliseLink($url), $uid, $id) || ($uid != 0)) { if (self::removeDuplicates(Strings::normaliseLink($url), $uid) || ($uid != 0)) {
return; return;
} }
@ -1895,15 +1922,14 @@ class Contact extends BaseObject
} }
/** /**
* @brief Helper function for "updateFromProbe". Remove duplicated contacts * @brief Remove duplicated contacts
* *
* @param string $nurl Normalised contact url * @param string $nurl Normalised contact url
* @param integer $uid User id * @param integer $uid User id
* @param integer $id Contact id of a duplicate
* @return boolean * @return boolean
* @throws \Exception * @throws \Exception
*/ */
private static function handleDuplicates($nurl, $uid, $id) public static function removeDuplicates(string $nurl, int $uid)
{ {
$condition = ['nurl' => $nurl, 'uid' => $uid, 'deleted' => false, 'network' => Protocol::FEDERATED]; $condition = ['nurl' => $nurl, 'uid' => $uid, 'deleted' => false, 'network' => Protocol::FEDERATED];
$count = DBA::count('contact', $condition); $count = DBA::count('contact', $condition);
@ -1918,7 +1944,7 @@ class Contact extends BaseObject
} }
$first = $first_contact['id']; $first = $first_contact['id'];
Logger::info('Found duplicates', ['count' => $count, 'id' => $id, 'first' => $first, 'uid' => $uid, 'nurl' => $nurl]); Logger::info('Found duplicates', ['count' => $count, 'first' => $first, 'uid' => $uid, 'nurl' => $nurl]);
if (($uid != 0 && ($first_contact['network'] == Protocol::DFRN))) { if (($uid != 0 && ($first_contact['network'] == Protocol::DFRN))) {
// Don't handle non public DFRN duplicates by now (legacy DFRN is very special because of the key handling) // Don't handle non public DFRN duplicates by now (legacy DFRN is very special because of the key handling)
Logger::info('Not handling non public DFRN duplicate', ['uid' => $uid, 'nurl' => $nurl]); Logger::info('Not handling non public DFRN duplicate', ['uid' => $uid, 'nurl' => $nurl]);
@ -2265,7 +2291,7 @@ class Contact extends BaseObject
$new_relation = (in_array($protocol, [Protocol::MAIL]) ? self::FRIEND : self::SHARING); $new_relation = (in_array($protocol, [Protocol::MAIL]) ? self::FRIEND : self::SHARING);
// create contact record // create contact record
DBA::insert('contact', [ self::insert([
'uid' => $uid, 'uid' => $uid,
'created' => DateTimeFormat::utcNow(), 'created' => DateTimeFormat::utcNow(),
'url' => $ret['url'], 'url' => $ret['url'],

View file

@ -200,7 +200,7 @@ class Diaspora
DBA::update('contact', $fields, $condition, $old); DBA::update('contact', $fields, $condition, $old);
} else { } else {
Logger::info('Create relay contact', ['fields' => $fields]); Logger::info('Create relay contact', ['fields' => $fields]);
DBA::insert('contact', $fields); Contact::insert($fields);
} }
} }