mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-27 20:48:46 +02:00
Use it for fast database cleanup (removal of all broken entries) when you don't want to try recovering inconsistent records. Note that you may need multiple report to eliminate all the inconsistencies.
510 lines
17 KiB
PHP
510 lines
17 KiB
PHP
<?php
|
|
// Copyright (c) 2010-2018 Combodo SARL
|
|
//
|
|
// This file is part of iTop.
|
|
//
|
|
// iTop is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// iTop is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
|
//
|
|
|
|
class DatabaseAnalyzer
|
|
{
|
|
var $iTimeLimitPerOperation;
|
|
|
|
public function __construct($iTimeLimitPerOperation = null)
|
|
{
|
|
$this->iTimeLimitPerOperation = $iTimeLimitPerOperation;
|
|
}
|
|
|
|
/**
|
|
* @param $sSelWrongRecs
|
|
* @param $sFixItRequest
|
|
* @param $sErrorDesc
|
|
* @param $sClass
|
|
* @param $aErrorsAndFixes
|
|
* @param array $aValueNames
|
|
*
|
|
* @throws \MySQLException
|
|
*/
|
|
private function ExecQuery($sSelWrongRecs, $sFixItRequest, $sErrorDesc, $sClass, &$aErrorsAndFixes, $aValueNames = array())
|
|
{
|
|
if (!is_null($this->iTimeLimitPerOperation))
|
|
{
|
|
set_time_limit($this->iTimeLimitPerOperation);
|
|
}
|
|
|
|
$aWrongRecords = CMDBSource::QueryToArray($sSelWrongRecs);
|
|
if (count($aWrongRecords) > 0)
|
|
{
|
|
foreach($aWrongRecords as $aRes)
|
|
{
|
|
if (!isset($aErrorsAndFixes[$sClass][$sErrorDesc]))
|
|
{
|
|
$aErrorsAndFixes[$sClass][$sErrorDesc] = array(
|
|
'count' => 1,
|
|
'query' => $sSelWrongRecs,
|
|
);
|
|
if (!empty($sFixItRequest))
|
|
{
|
|
$aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'] = array($sFixItRequest);
|
|
$aErrorsAndFixes[$sClass][$sErrorDesc]['cleanup'] = array($sFixItRequest);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$aErrorsAndFixes[$sClass][$sErrorDesc]['count'] += 1;
|
|
}
|
|
if (empty($aValueNames))
|
|
{
|
|
$aValues = array('id' => $aRes['id']);
|
|
}
|
|
else
|
|
{
|
|
$aValues = array();
|
|
foreach ($aValueNames as $sValueName)
|
|
{
|
|
$aValues[$sValueName] = $aRes[$sValueName];
|
|
}
|
|
}
|
|
|
|
if (isset($aRes['value']))
|
|
{
|
|
$value = $aRes['value'];
|
|
$aValues['value'] = $value;
|
|
if (!isset($aErrorsAndFixes[$sClass][$sErrorDesc]['values'][$value]))
|
|
{
|
|
$aErrorsAndFixes[$sClass][$sErrorDesc]['values'][$value] = 1;
|
|
}
|
|
else
|
|
{
|
|
$aErrorsAndFixes[$sClass][$sErrorDesc]['values'][$value] += 1;
|
|
}
|
|
}
|
|
$aErrorsAndFixes[$sClass][$sErrorDesc]['res'][] = $aValues;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param $aClassSelection
|
|
*
|
|
* @return array
|
|
* @throws \CoreException
|
|
* @throws \MySQLException
|
|
* @throws \Exception
|
|
*/
|
|
public function CheckIntegrity($aClassSelection)
|
|
{
|
|
$aErrorsAndFixes = array();
|
|
|
|
if (empty($aClassSelection))
|
|
{
|
|
$aClassSelection = MetaModel::GetClasses();
|
|
}
|
|
else
|
|
{
|
|
$aClasses = $aClassSelection;
|
|
foreach($aClasses as $sClass)
|
|
{
|
|
$aExpectedClasses = MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL);
|
|
$aClassSelection = array_merge($aClassSelection, $aExpectedClasses);
|
|
}
|
|
$aClassSelection = array_unique($aClassSelection);
|
|
}
|
|
|
|
foreach ($aClassSelection as $sClass)
|
|
{
|
|
// Check uniqueness rules
|
|
$this->CheckUniquenessRules($sClass, $aErrorsAndFixes);
|
|
|
|
if (!MetaModel::HasTable($sClass))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
$sRootClass = MetaModel::GetRootClass($sClass);
|
|
$sTable = MetaModel::DBGetTable($sClass);
|
|
$sKeyField = MetaModel::DBGetKey($sClass);
|
|
|
|
if (!MetaModel::IsStandaloneClass($sClass))
|
|
{
|
|
$sRootTable = MetaModel::DBGetTable($sRootClass);
|
|
$sRootKey = MetaModel::DBGetKey($sRootClass);
|
|
if (!MetaModel::IsRootClass($sClass))
|
|
{
|
|
$this->CheckRecordsInRootTable($sTable, $sKeyField, $sRootTable, $sRootKey, $sClass, $aErrorsAndFixes);
|
|
$this->CheckRecordsInChildTable($sRootClass, $sClass, $sRootTable, $sRootKey, $sTable, $sKeyField, $aErrorsAndFixes);
|
|
if (!MetaModel::IsLeafClass($sClass))
|
|
{
|
|
$this->CheckIntermediateFinalClass($sRootClass, $sClass, $sRootTable, $sRootKey, $sTable, $sKeyField, $aErrorsAndFixes);
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
|
{
|
|
// Skip this attribute if not defined in this table
|
|
if (!MetaModel::IsAttributeOrigin($sClass, $sAttCode))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ($oAttDef->IsExternalKey())
|
|
{
|
|
$this->CheckExternalKeys($oAttDef, $sTable, $sKeyField, $sAttCode, $sClass, $aErrorsAndFixes);
|
|
}
|
|
elseif ($oAttDef->IsDirectField() && !($oAttDef instanceof AttributeTagSet))
|
|
{
|
|
$this->CheckEnums($sClass, $sAttCode, $oAttDef, $sTable, $sKeyField, $aErrorsAndFixes);
|
|
}
|
|
}
|
|
}
|
|
$this->CheckUsers($aErrorsAndFixes);
|
|
|
|
return $aErrorsAndFixes;
|
|
}
|
|
|
|
/**
|
|
* @param $sClass
|
|
* @param $sUniquenessRuleId
|
|
* @param $aUniquenessRuleProperties
|
|
* @param $aErrorsAndFixes
|
|
*
|
|
* @throws \CoreException
|
|
* @throws \MissingQueryArgument
|
|
* @throws \MySQLException
|
|
* @throws \OQLException
|
|
* @throws \Exception
|
|
*/
|
|
private function CheckUniquenessRule($sClass, $sUniquenessRuleId, $aUniquenessRuleProperties, &$aErrorsAndFixes)
|
|
{
|
|
$sOqlUniquenessQuery = "SELECT $sClass";
|
|
if (!(empty($sUniquenessFilter = $aUniquenessRuleProperties['filter'])))
|
|
{
|
|
$sOqlUniquenessQuery .= ' WHERE '.$sUniquenessFilter;
|
|
}
|
|
$oUniquenessQuery = DBObjectSearch::FromOQL($sOqlUniquenessQuery);
|
|
|
|
$aValueNames = array();
|
|
$aGroupByExpr = array();
|
|
foreach ($aUniquenessRuleProperties['attributes'] as $sAttributeCode)
|
|
{
|
|
$oExpr = Expression::FromOQL("$sClass.$sAttributeCode");
|
|
$aGroupByExpr[$sAttributeCode] = $oExpr;
|
|
$aValueNames[] = $sAttributeCode;
|
|
}
|
|
|
|
$aSelectExpr = array();
|
|
|
|
$sSQLUniquenessQuery = $oUniquenessQuery->MakeGroupByQuery(array(), $aGroupByExpr, false, $aSelectExpr);
|
|
|
|
$sSQLUniquenessQuery .= ' having count(*) > 1';
|
|
|
|
$sErrorDesc = $this->GetUniquenessRuleMessage($sUniquenessRuleId);
|
|
|
|
$this->ExecQuery($sSQLUniquenessQuery, '', $sErrorDesc, $sClass, $aErrorsAndFixes, $aValueNames);
|
|
if (isset($aErrorsAndFixes[$sClass][$sErrorDesc]['res']))
|
|
{
|
|
$aFixit = array("-- In order to get the duplicates, run the following queries:");
|
|
foreach ($aErrorsAndFixes[$sClass][$sErrorDesc]['res'] as $aValues)
|
|
{
|
|
$oFixSearch = new DBObjectSearch($sClass);
|
|
foreach ($aValues as $sAttCode => $sValue)
|
|
{
|
|
$oFixSearch->AddCondition($sAttCode, $sValue, '=');
|
|
}
|
|
$aFixit[] = $oFixSearch->MakeSelectQuery().';';
|
|
$aFixit[] = "";
|
|
}
|
|
$aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'] = $aFixit;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
private function GetUniquenessRuleMessage($sUniquenessRuleId)
|
|
{
|
|
// we could add also a specific message if user is admin ("dict key is missing")
|
|
return Dict::Format('Core:UniquenessDefaultError', $sUniquenessRuleId);
|
|
}
|
|
|
|
/**
|
|
* @param $sClass
|
|
* @param array $aErrorsAndFixes
|
|
*
|
|
* @throws \CoreException
|
|
*/
|
|
private function CheckUniquenessRules($sClass, &$aErrorsAndFixes)
|
|
{
|
|
if (method_exists('MetaModel', 'GetUniquenessRules'))
|
|
{
|
|
$aUniquenessRules = MetaModel::GetUniquenessRules($sClass);
|
|
foreach ($aUniquenessRules as $sUniquenessRuleId => $aUniquenessRuleProperties)
|
|
{
|
|
if ($aUniquenessRuleProperties['disabled'] === true)
|
|
{
|
|
continue;
|
|
}
|
|
$this->CheckUniquenessRule($sClass, $sUniquenessRuleId, $aUniquenessRuleProperties, $aErrorsAndFixes);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check that any record found here has its counterpart in the root table
|
|
*
|
|
* @param $sTable
|
|
* @param $sKeyField
|
|
* @param $sRootTable
|
|
* @param $sRootKey
|
|
* @param $sClass
|
|
* @param array $aErrorsAndFixes
|
|
*
|
|
* @throws \MySQLException
|
|
*/
|
|
private function CheckRecordsInRootTable($sTable, $sKeyField, $sRootTable, $sRootKey, $sClass, &$aErrorsAndFixes)
|
|
{
|
|
$sSelect = "SELECT DISTINCT `$sTable`.`$sKeyField` AS id";
|
|
$sDelete = "DELETE `$sTable`";
|
|
$sFilter = "FROM `$sTable` LEFT JOIN `$sRootTable` ON `$sTable`.`$sKeyField` = `$sRootTable`.`$sRootKey` WHERE `$sRootTable`.`$sRootKey` IS NULL";
|
|
$sSelectWrongRecs = "$sSelect $sFilter";
|
|
$sFixItRequest = "$sDelete $sFilter";
|
|
$this->ExecQuery($sSelectWrongRecs, $sFixItRequest, Dict::Format('DBAnalyzer-Integrity-OrphanRecord', $sTable, $sRootTable), $sClass, $aErrorsAndFixes);
|
|
}
|
|
|
|
/**
|
|
* Check that any record found in the root table and referring to a child class
|
|
* has its counterpart here (detect orphan nodes -root or in the middle of the hierarchy)
|
|
*
|
|
* @param $sRootClass
|
|
* @param $sClass
|
|
* @param $sRootTable
|
|
* @param $sRootKey
|
|
* @param $sTable
|
|
* @param $sKeyField
|
|
* @param array $aErrorsAndFixes
|
|
*
|
|
* @throws \CoreException
|
|
* @throws \MySQLException
|
|
*/
|
|
private function CheckRecordsInChildTable($sRootClass, $sClass, $sRootTable, $sRootKey, $sTable, $sKeyField, &$aErrorsAndFixes)
|
|
{
|
|
$sFinalClassField = MetaModel::DBGetClassField($sRootClass);
|
|
$aExpectedClasses = MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL);
|
|
$sExpectedClasses = implode(",", CMDBSource::Quote($aExpectedClasses, true));
|
|
$sSelect = "SELECT DISTINCT `$sRootTable`.`$sRootKey` AS id";
|
|
$sDelete = "DELETE `$sRootTable`";
|
|
$sFilter = "FROM `$sRootTable` LEFT JOIN `$sTable` ON `$sRootTable`.`$sRootKey` = `$sTable`.`$sKeyField` WHERE `$sTable`.`$sKeyField` IS NULL AND `$sRootTable`.`$sFinalClassField` IN ($sExpectedClasses)";
|
|
$sSelWrongRecs = "$sSelect $sFilter";
|
|
$sFixItRequest = "$sDelete $sFilter";
|
|
$this->ExecQuery($sSelWrongRecs, $sFixItRequest, Dict::Format('DBAnalyzer-Integrity-OrphanRecord', $sRootTable, $sTable), $sRootClass, $aErrorsAndFixes);
|
|
}
|
|
|
|
/**
|
|
* Check that the "finalclass" field is correct for all the classes of the hierarchy
|
|
*
|
|
* @param $sRootClass
|
|
* @param $sClass
|
|
* @param $sRootTable
|
|
* @param $sRootKey
|
|
* @param $sTable
|
|
* @param $sKeyField
|
|
* @param $aErrorsAndFixes
|
|
*
|
|
* @throws \CoreException
|
|
*/
|
|
private function CheckIntermediateFinalClass($sRootClass, $sClass, $sRootTable, $sRootKey, $sTable, $sKeyField, &$aErrorsAndFixes)
|
|
{
|
|
$sField = MetaModel::DBGetClassField($sClass);
|
|
$sRootField = MetaModel::DBGetClassField($sRootClass);
|
|
$sSelWrongRecs = <<<SQL
|
|
SELECT `$sTable`.`$sKeyField` AS id
|
|
FROM `$sTable`
|
|
JOIN `$sRootTable` ON `$sRootTable`.`$sRootKey` = `$sTable`.`$sKeyField`
|
|
WHERE `$sTable`.`$sField` != `$sRootTable`.`$sRootField`
|
|
SQL;
|
|
// Copy the finalclass of the root table
|
|
$sFixItRequest = "UPDATE `$sTable`,`$sRootTable` SET `$sTable`.`$sField` = `$sRootTable`.`$sRootField` WHERE `$sTable`.`$sKeyField` = `$sRootTable`.`$sRootKey`";
|
|
$this->ExecQuery($sSelWrongRecs, $sFixItRequest, Dict::Format('DBAnalyzer-Integrity-FinalClass', $sField, $sTable, $sRootTable), $sClass, $aErrorsAndFixes);
|
|
}
|
|
/**
|
|
* Check that any external field is pointing to an existing object
|
|
*
|
|
* @param \AttributeDefinition $oAttDef
|
|
* @param $sTable
|
|
* @param $sKeyField
|
|
* @param $sAttCode
|
|
* @param $sClass
|
|
* @param array $aErrorsAndFixes
|
|
*
|
|
* @throws \CoreException
|
|
* @throws \MySQLException
|
|
*/
|
|
private function CheckExternalKeys(AttributeDefinition $oAttDef, $sTable, $sKeyField, $sAttCode, $sClass, &$aErrorsAndFixes)
|
|
{
|
|
$sRemoteClass = $oAttDef->GetTargetClass();
|
|
$sRemoteTable = MetaModel::DBGetTable($sRemoteClass);
|
|
$sRemoteKey = MetaModel::DBGetKey($sRemoteClass);
|
|
|
|
$aCols = $oAttDef->GetSQLExpressions(); // Workaround a PHP bug: sometimes issuing a Notice if invoking current(somefunc())
|
|
$sExtKeyField = current($aCols); // get the first column for an external key
|
|
|
|
// Note: a class/table may have an external key on itself
|
|
$sSelect = "SELECT DISTINCT `$sTable`.`$sKeyField` AS id, `$sTable`.`$sExtKeyField` AS value";
|
|
$sFilter = "FROM `$sTable` LEFT JOIN `$sRemoteTable` AS `{$sRemoteTable}_1` ON `$sTable`.`$sExtKeyField` = `{$sRemoteTable}_1`.`$sRemoteKey`";
|
|
|
|
$sFilter = $sFilter." WHERE `{$sRemoteTable}_1`.`$sRemoteKey` IS NULL";
|
|
// Exclude the records pointing to 0/null from the errors (separate test below)
|
|
$sFilter .= " AND `$sTable`.`$sExtKeyField` IS NOT NULL";
|
|
$sFilter .= " AND `$sTable`.`$sExtKeyField` != 0";
|
|
|
|
$sSelWrongRecs = "$sSelect $sFilter";
|
|
|
|
$sErrorDesc = Dict::Format('DBAnalyzer-Integrity-InvalidExtKey', $sAttCode, $sTable, $sExtKeyField);
|
|
$this->ExecQuery($sSelWrongRecs, '', $sErrorDesc, $sClass, $aErrorsAndFixes);
|
|
// Fix it request needs the values of the enum to generate the requests
|
|
if (isset($aErrorsAndFixes[$sClass][$sErrorDesc]['values']))
|
|
{
|
|
$aFixIt = array();
|
|
$aFixIt[] = "-- Remove inconsistant entries:";
|
|
$iOffset = 0;
|
|
$iStep = 100;
|
|
do
|
|
{
|
|
$aIds = array_slice(array_keys($aErrorsAndFixes[$sClass][$sErrorDesc]['values']), $iOffset, $iStep);
|
|
$sIds = implode(', ', $aIds);
|
|
$sDelete = "DELETE `$sTable` FROM `$sTable` WHERE `$sTable`.`$sExtKeyField` IN ($sIds)";
|
|
$aFixIt[] = $sDelete;
|
|
$aErrorsAndFixes[$sClass][$sErrorDesc]['cleanup'][] = $sDelete;
|
|
$iOffset += $iStep;
|
|
}
|
|
while (count($aIds) == $iStep);
|
|
$aFixIt[] = "";
|
|
$aFixIt[] = "-- Or fix inconsistant values: Replace XXX with the appropriate value";
|
|
foreach (array_keys($aErrorsAndFixes[$sClass][$sErrorDesc]['values']) as $sKey)
|
|
{
|
|
$aFixIt[] = "UPDATE `$sTable` SET `$sTable`.`$sExtKeyField` = XXX WHERE `$sTable`.`$sExtKeyField` = '$sKey'";
|
|
}
|
|
$aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'] = $aFixIt;
|
|
}
|
|
|
|
if (!$oAttDef->IsNullAllowed())
|
|
{
|
|
$sSelect = "SELECT DISTINCT `$sTable`.`$sKeyField` AS id";
|
|
$sDelete = "DELETE `$sTable`";
|
|
$sFilter = "FROM `$sTable` WHERE `$sTable`.`$sExtKeyField` IS NULL OR `$sTable`.`$sExtKeyField` = 0";
|
|
$sSelWrongRecs = "$sSelect $sFilter";
|
|
$sFixItRequest = "$sDelete $sFilter";
|
|
$sErrorDesc = Dict::Format('DBAnalyzer-Integrity-MissingExtKey', $sAttCode, $sTable, $sExtKeyField);
|
|
$this->ExecQuery($sSelWrongRecs, $sFixItRequest, $sErrorDesc, $sClass, $aErrorsAndFixes);
|
|
if (isset($aErrorsAndFixes[$sClass][$sErrorDesc]['count']) && ($aErrorsAndFixes[$sClass][$sErrorDesc]['count'] > 0))
|
|
{
|
|
$aFixIt = $aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'];
|
|
$aFixIt[] = "-- Alternate fix";
|
|
$aFixIt[] = "-- Replace XXX with the appropriate value";
|
|
$aFixIt[] = "UPDATE `$sTable` SET `$sTable`.`$sExtKeyField` = XXX WHERE `$sTable`.`$sExtKeyField` IS NULL OR `$sTable`.`$sExtKeyField` = 0";
|
|
$aAdditionalFixIt = $this->GetSpecificExternalKeysFixItForNull($sTable, $sExtKeyField);
|
|
foreach ($aAdditionalFixIt as $sFixIt)
|
|
{
|
|
$aFixIt[] = $sFixIt;
|
|
}
|
|
$aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'] = $aFixIt;
|
|
}
|
|
}
|
|
}
|
|
|
|
private function GetSpecificExternalKeysFixItForNull($sTable, $sExtKeyField)
|
|
{
|
|
$aFixIt = array();
|
|
if ($sTable == 'ticket' && $sExtKeyField == 'org_id')
|
|
{
|
|
$aFixIt[] = "-- Alternate fix: set the ticket org to the caller org";
|
|
$aFixIt[] = "UPDATE ticket AS t JOIN contact AS c ON t.caller_id=c.id SET t.org_id=c.org_id WHERE t.org_id IS NULL OR t.org_id = 0";
|
|
}
|
|
return $aFixIt;
|
|
}
|
|
|
|
/**
|
|
* Check that the values fit the allowed values
|
|
*
|
|
* @param $sClass
|
|
* @param $sAttCode
|
|
* @param \AttributeDefinition $oAttDef
|
|
* @param $sTable
|
|
* @param $sKeyField
|
|
* @param array $aErrorsAndFixes
|
|
*
|
|
* @throws \MySQLException
|
|
* @throws \Exception
|
|
*/
|
|
private function CheckEnums($sClass, $sAttCode, AttributeDefinition $oAttDef, $sTable, $sKeyField, &$aErrorsAndFixes)
|
|
{
|
|
$aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode);
|
|
if (!is_null($aAllowedValues) && count($aAllowedValues) > 0)
|
|
{
|
|
$sExpectedValues = implode(",", CMDBSource::Quote(array_keys($aAllowedValues), true));
|
|
|
|
$aCols = $oAttDef->GetSQLExpressions(); // Workaround a PHP bug: sometimes issuing a Notice if invoking current(somefunc())
|
|
$sMyAttributeField = current($aCols); // get the first column for the moment
|
|
$sFilter = "FROM `$sTable` WHERE `$sTable`.`$sMyAttributeField` NOT IN ($sExpectedValues)";
|
|
$sDelete = "DELETE `$sTable`";
|
|
$sSelect = "SELECT DISTINCT `$sTable`.`$sKeyField` AS id, `$sTable`.`$sMyAttributeField` AS value";
|
|
$sSelWrongRecs = "$sSelect $sFilter";
|
|
$sFixItRequest = "$sDelete $sFilter";
|
|
$sErrorDesc = Dict::Format('DBAnalyzer-Integrity-InvalidValue', $sAttCode, $sTable, $sMyAttributeField);
|
|
$this->ExecQuery($sSelWrongRecs, $sFixItRequest, $sErrorDesc, $sClass, $aErrorsAndFixes);
|
|
// Fix it request needs the values of the enum to generate the requests
|
|
if (isset($aErrorsAndFixes[$sClass][$sErrorDesc]['values']))
|
|
{
|
|
if (isset($aErrorsAndFixes[$sClass][$sErrorDesc]['fixit']))
|
|
{
|
|
$aFixIt = $aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'];
|
|
$aFixIt[] = "-- Alternative: Replace 'XXX' with the appropriate value";
|
|
}
|
|
else
|
|
{
|
|
$aFixIt = array("-- Replace 'XXX' with the appropriate value");
|
|
}
|
|
foreach (array_keys($aErrorsAndFixes[$sClass][$sErrorDesc]['values']) as $sKey)
|
|
{
|
|
$aFixIt[] = "UPDATE `$sTable` SET `$sTable`.`$sMyAttributeField` = 'XXX' WHERE `$sTable`.`$sMyAttributeField` = '$sKey'";
|
|
}
|
|
$aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'] = $aFixIt;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check user accounts without profile
|
|
*
|
|
* @param $aErrorsAndFixes
|
|
*
|
|
* @throws \CoreException
|
|
* @throws \MySQLException
|
|
*/
|
|
private function CheckUsers(&$aErrorsAndFixes)
|
|
{
|
|
$sClass = 'User';
|
|
$sUserTable = MetaModel::DBGetTable($sClass);
|
|
$sLinkTable = MetaModel::DBGetTable('URP_UserProfile');
|
|
$sSelect = "SELECT DISTINCT u.id AS id, u.`login` AS value";
|
|
$sFilter = "FROM `$sUserTable` AS u LEFT JOIN `$sLinkTable` AS l ON l.userid = u.id WHERE l.id IS NULL";
|
|
$sSelWrongRecs = "$sSelect $sFilter";
|
|
$sFixit = "-- Remove the corresponding user(s)";
|
|
$sErrorDesc = Dict::S('DBAnalyzer-Integrity-UsersWithoutProfile');
|
|
$this->ExecQuery($sSelWrongRecs, $sFixit, $sErrorDesc, $sClass, $aErrorsAndFixes);
|
|
}
|
|
|
|
|
|
}
|