diff --git a/datamodels/2.x/combodo-db-tools/db_analyzer.class.inc.php b/datamodels/2.x/combodo-db-tools/db_analyzer.class.inc.php index 61330725a..b4807230d 100644 --- a/datamodels/2.x/combodo-db-tools/db_analyzer.class.inc.php +++ b/datamodels/2.x/combodo-db-tools/db_analyzer.class.inc.php @@ -104,10 +104,6 @@ class DatabaseAnalyzer */ public function CheckIntegrity($aClassSelection) { - // Getting and setting time limit are not symetric: - // www.php.net/manual/fr/function.set-time-limit.php#72305 - $iPreviousTimeLimit = ini_get('max_execution_time'); - $aErrorsAndFixes = array(); if (empty($aClassSelection)) @@ -125,7 +121,25 @@ class DatabaseAnalyzer $aClassSelection = array_unique($aClassSelection); } - foreach($aClassSelection as $sClass) + // First loop for root classes (other classes are based on root classes) + foreach ($aClassSelection as $sClass) + { + if (!MetaModel::HasTable($sClass)) + { + continue; + } + + if (MetaModel::IsRootClass($sClass) && !MetaModel::IsStandaloneClass($sClass)) + { + $sRootClass = MetaModel::GetRootClass($sClass); + $sRootTable = MetaModel::DBGetTable($sRootClass); + $sRootKey = MetaModel::DBGetKey($sRootClass); + // Control that the finalclass is a leaf child class + $this->CheckRootFinalClass($sRootClass, $sRootTable, $sRootKey, $aErrorsAndFixes); + } + } + + foreach ($aClassSelection as $sClass) { // Check uniqueness rules $this->CheckUniquenessRules($sClass, $aErrorsAndFixes); @@ -141,16 +155,15 @@ class DatabaseAnalyzer if (!MetaModel::IsStandaloneClass($sClass)) { + $sRootTable = MetaModel::DBGetTable($sRootClass); + $sRootKey = MetaModel::DBGetKey($sRootClass); if (!MetaModel::IsRootClass($sClass)) { - $sRootTable = MetaModel::DBGetTable($sRootClass); - $sRootKey = MetaModel::DBGetKey($sRootClass); - $this->CheckRecordsInRootTable($sTable, $sKeyField, $sRootTable, $sRootKey, $sClass, $aErrorsAndFixes); $this->CheckRecordsInChildTable($sRootClass, $sClass, $sRootTable, $sRootKey, $sTable, $sKeyField, $aErrorsAndFixes); if (!MetaModel::IsLeafClass($sClass)) { - $this->CheckFinalClass($sRootClass, $sClass, $sRootTable, $sRootKey, $sTable, $sKeyField, $aErrorsAndFixes); + $this->CheckIntermediateFinalClass($sRootClass, $sClass, $sRootTable, $sRootKey, $sTable, $sKeyField, $aErrorsAndFixes); } } } @@ -175,10 +188,6 @@ class DatabaseAnalyzer } $this->CheckUsers($aErrorsAndFixes); - if (!is_null($this->iTimeLimitPerOperation)) - { - set_time_limit($iPreviousTimeLimit); - } return $aErrorsAndFixes; } @@ -318,6 +327,51 @@ class DatabaseAnalyzer $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 $sRootTable + * @param $sRootKey + * @param $aErrorsAndFixes + * + * @throws \CoreException + */ + private function CheckRootFinalClass($sRootClass, $sRootTable, $sRootKey, &$aErrorsAndFixes) + { + $aLeafClasses = array(); + $aAllowedValues = MetaModel::EnumChildClasses($sRootClass, ENUM_CHILD_CLASSES_ALL); + foreach ($aAllowedValues as $sAllowedClass) + { + if (MetaModel::IsLeafClass($sAllowedClass)) + { + $aLeafClasses[] = $sAllowedClass; + } + } + if (empty($aLeafClasses)) + { + return; + } + $sLeafClasses = implode(",", CMDBSource::Quote($aLeafClasses, true)); + $sRootField = MetaModel::DBGetClassField($sRootClass); + $sSelect = "SELECT DISTINCT `$sRootTable`.`$sRootKey` AS id"; + $sDelete = "DELETE `$sRootTable`"; + $sFilter = "FROM `$sRootTable` WHERE `$sRootTable`.`$sRootField` NOT IN ($sLeafClasses)"; + $sSelWrongRecs = "$sSelect $sFilter"; + $sFixItRequest = ''; + foreach ($aLeafClasses as $sLeafClass) + { + $sTable = MetaModel::DBGetTable($sLeafClass); + $sKey = MetaModel::DBGetKey($sLeafClass); + $sFixItRequest .= <<ExecQuery($sSelWrongRecs, $sFixItRequest, Dict::Format('DBAnalyzer-Integrity-RootFinalClass', $sRootField, $sRootTable), $sRootClass, $aErrorsAndFixes); + } + /** * Check that the "finalclass" field is correct for all the classes of the hierarchy * @@ -331,7 +385,7 @@ class DatabaseAnalyzer * * @throws \CoreException */ - private function CheckFinalClass($sRootClass, $sClass, $sRootTable, $sRootKey, $sTable, $sKeyField, &$aErrorsAndFixes) + private function CheckIntermediateFinalClass($sRootClass, $sClass, $sRootTable, $sRootKey, $sTable, $sKeyField, &$aErrorsAndFixes) { $sField = MetaModel::DBGetClassField($sClass); $sRootField = MetaModel::DBGetClassField($sRootClass); @@ -345,7 +399,6 @@ SQL; $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 * @@ -484,6 +537,8 @@ SQL; } /** + * Check user accounts without profile + * * @param $aErrorsAndFixes * * @throws \CoreException @@ -491,7 +546,6 @@ SQL; */ private function CheckUsers(&$aErrorsAndFixes) { -// Check user accounts without profile $sUserTable = MetaModel::DBGetTable('User'); $sLinkTable = MetaModel::DBGetTable('URP_UserProfile'); $sSelect = "SELECT DISTINCT u.id AS id, u.`login` AS value"; diff --git a/datamodels/2.x/combodo-db-tools/en.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/en.dict.combodo-db-tools.php index 91e83d5d1..faeb87d33 100644 --- a/datamodels/2.x/combodo-db-tools/en.dict.combodo-db-tools.php +++ b/datamodels/2.x/combodo-db-tools/en.dict.combodo-db-tools.php @@ -53,6 +53,7 @@ Dict::Add('EN US', 'English', 'English', array( 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all', '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 than `%3$s`.`%1$s`', + 'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class', )); // Database Info diff --git a/datamodels/2.x/combodo-db-tools/fr.dict.combodo-db-tools.php b/datamodels/2.x/combodo-db-tools/fr.dict.combodo-db-tools.php index d109626af..bc683642c 100644 --- a/datamodels/2.x/combodo-db-tools/fr.dict.combodo-db-tools.php +++ b/datamodels/2.x/combodo-db-tools/fr.dict.combodo-db-tools.php @@ -48,6 +48,7 @@ Dict::Add('FR FR', 'French', 'Français', array( 'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Certains comptes utilisateurs n\'ont aucun profile', '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', )); // Database Info