N°2527 - Manage hierarchical keys in database maintenance tools

This commit is contained in:
Eric Espie
2021-10-04 11:34:19 +02:00
parent eb239843aa
commit 8c39374abb
7 changed files with 81 additions and 17 deletions

View File

@@ -0,0 +1,34 @@
<?php
/*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* Rebuild the hierarchical keys control data
*/
use Combodo\iTop\Core\MetaModel\HierarchicalKey;
require_once('../approot.inc.php');
require_once APPROOT.'application/startup.inc.php';
foreach(MetaModel::GetClasses() as $sClass)
{
if (!MetaModel::HasTable($sClass))
{
continue;
}
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
{
// Check (once) all the attributes that are hierarchical keys
if ((MetaModel::GetAttributeOrigin($sClass, $sAttCode) == $sClass) && $oAttDef->IsHierarchicalKey())
{
echo "Rebuild hierarchical key $sAttCode from $sClass.\n";
HierarchicalKey::Rebuild($sClass, $sAttCode, $oAttDef);
}
}
}
echo "Done\n";

View File

@@ -17,6 +17,8 @@
// along with iTop. If not, see <http://www.gnu.org/licenses/>
//
use Combodo\iTop\Core\MetaModel\HierarchicalKey;
class DatabaseAnalyzer
{
var $iTimeLimitPerOperation;
@@ -162,6 +164,9 @@ class DatabaseAnalyzer
if ($oAttDef->IsExternalKey())
{
$this->CheckExternalKeys($oAttDef, $sTable, $sKeyField, $sAttCode, $sClass, $aErrorsAndFixes);
if ((MetaModel::GetAttributeOrigin($sClass, $sAttCode) == $sClass) && $oAttDef->IsHierarchicalKey()) {
$this->CheckHK($sClass, $sAttCode, $aErrorsAndFixes);
}
}
elseif ($oAttDef->IsDirectField() && !($oAttDef instanceof AttributeTagSet))
{
@@ -506,5 +511,24 @@ class DatabaseAnalyzer
$this->ExecQuery($sSelWrongRecs, $sFixit, $sErrorDesc, $sClass, $aErrorsAndFixes);
}
/**
* Check hierarchical keys
*
* @param $sClass
* @param $sAttCode
* @param $aErrorsAndFixes
*
* @throws \Exception
*/
private function CheckHK($sClass, $sAttCode, &$aErrorsAndFixes)
{
try {
HierarchicalKey::VerifyIntegrity($sClass, $sAttCode, MetaModel::GetAttributeDef($sClass, $sAttCode));
} catch (CoreException $e) {
$sErrorDesc = Dict::Format('DBAnalyzer-Integrity-HKInvalid', $sAttCode);
$aErrorsAndFixes[$sClass][$sErrorDesc]['count'] = 1;
$aErrorsAndFixes[$sClass][$sErrorDesc]['query'] = '-- N/A';
$aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'] = ['-- Run script env-'.utils::GetCurrentEnvironment().DIRECTORY_SEPARATOR.'combodo-db-tools'.DIRECTORY_SEPARATOR.'bin'.DIRECTORY_SEPARATOR.'rebuildhk.php' ];
}
}
}

View File

@@ -234,17 +234,19 @@ function DisplayErrorDetails($aResults, $bVerbose)
$oFieldSet = FieldSetUIBlockFactory::MakeStandard(Dict::S('DBTools:SQLquery'));
$oCollapsible->AddSubBlock($oFieldSet);
$oCode = UIContentBlockUIBlockFactory::MakeForPreformatted($aError['query']);
$oFieldSet->AddSubBlock($oCode);
if (array_key_exists('query', $aError)) {
$oCode = UIContentBlockUIBlockFactory::MakeForPreformatted($aError['query']);
$oFieldSet->AddSubBlock($oCode);
if (isset($aError['fixit'])) {
$oFieldSet = FieldSetUIBlockFactory::MakeStandard(Dict::S('DBTools:FixitSQLquery'));
$oCollapsible->AddSubBlock($oFieldSet);
if (isset($aError['fixit'])) {
$oFieldSet = FieldSetUIBlockFactory::MakeStandard(Dict::S('DBTools:FixitSQLquery'));
$oCollapsible->AddSubBlock($oFieldSet);
$aQueries = $aError['fixit'];
foreach ($aQueries as $sFixQuery) {
$oCode = UIContentBlockUIBlockFactory::MakeForPreformatted($sFixQuery);
$oFieldSet->AddSubBlock($oCode);
$aQueries = $aError['fixit'];
foreach ($aQueries as $sFixQuery) {
$oCode = UIContentBlockUIBlockFactory::MakeForPreformatted($sFixQuery);
$oFieldSet->AddSubBlock($oCode);
}
}
}

View File

@@ -55,6 +55,7 @@ Dict::Add('EN US', 'English', 'English', array(
'DBAnalyzer-Integrity-MissingExtKey' => 'Missing external key %1$s (column: `%2$s.%3$s`)',
'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)',
'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all',
'DBAnalyzer-Integrity-HKInvalid' => 'Broken hierarchical key `%1$s`',
'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted',
'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value as `%3$s`.`%1$s`',
'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class',

View File

@@ -46,10 +46,11 @@ Dict::Add('FR FR', 'French', 'Français', array(
'DBTools:DetailedErrorTitle' => '%2$s erreur(s) dans la classe %1$s : %3$s',
'DBAnalyzer-Integrity-OrphanRecord' => 'Enregistrement orphelin dans `%1$s`, il devrait avoir son équivalent dans la table `%2$s`',
'DBAnalyzer-Integrity-InvalidExtKey' => 'Clef externe invalide %1$s (colonne: `%2$s.%3$s`)',
'DBAnalyzer-Integrity-MissingExtKey' => 'Clef externe manquante %1$s (colonne: `%2$s.%3$s`)',
'DBAnalyzer-Integrity-InvalidExtKey' => 'Clé externe invalide %1$s (colonne: `%2$s.%3$s`)',
'DBAnalyzer-Integrity-MissingExtKey' => 'Clé externe manquante %1$s (colonne: `%2$s.%3$s`)',
'DBAnalyzer-Integrity-InvalidValue' => 'Valeur invalide pour %1$s (colonne: `%2$s.%3$s`)',
'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Certains comptes utilisateurs n\'ont aucun profile',
'DBAnalyzer-Integrity-HKInvalid' => 'Clé hiérarchique `%1$s` invalide',
'DBAnalyzer-Fetch-Count-Error' => 'Erreur de récupération dans `%1$s`, %2$d enregistrements récupérés / %3$d comptés',
'DBAnalyzer-Integrity-FinalClass' => 'Le champ `%2$s`.`%1$s` doit avoir la même valeur que `%3$s`.`%1$s`',
'DBAnalyzer-Integrity-RootFinalClass' => 'Le champ `%2$s`.`%1$s` doit contenir une classe valide',

View File

@@ -40,9 +40,11 @@ class DBAnalyzerUtils
$iCount = $aError['count'];
fwrite($fReport, '-- Count: '.$iCount."\r\n");
fwrite($fReport, '-- Error: '.$sErrorLabel."\r\n");
$sQuery = $aError['query'];
fwrite($fReport, '-- Query: '.$sQuery."\r\n");
if (array_key_exists('query', $aError)) {
$sQuery = $aError['query'];
fwrite($fReport, '-- Query: '.$sQuery."\r\n");
}
if (isset($aError['fixit']))
{
fwrite($fReport, "\r\n-- Fix it (indication):\r\n\r\n");

View File

@@ -44,8 +44,8 @@ class HierarchicalKey {
$iMinLeft = $iParentLeft;
foreach ($aTreeIds as $aValues) {
$iChildLeft = $aValues[$sLeft];
$iChildRight = $aValues[$sRight];
$iChildLeft = $aValues['ctrl_left'];
$iChildRight = $aValues['ctrl_right'];
if ($iChildLeft <= $iMinLeft || $iParentRight <= $iChildRight || $iChildRight <= $iChildLeft) {
throw new CoreException("Wrong HK control values");
}