/**
  * Helper to modify an enum value	
  * The change is made in the datamodel definition, but the value has to be changed in the DB as well	 	 
  * Must be called BEFORE DB update, i.e within an implementation of BeforeDatabaseCreation()
  * This helper does change ONE value at a time	 
  * 	 
  * @param string $sClass A valid class name
  * @param string $sAttCode The enum attribute code
  * @param string $sFrom Original value (already INVALID in the current datamodel)	 	
  * @param string $sTo New value (valid in the current datamodel)
  * @return void	 	 	
  */
 public static function RenameEnumValueInDB($sClass, $sAttCode, $sFrom, $sTo)
 {
     try {
         $sOriginClass = MetaModel::GetAttributeOrigin($sClass, $sAttCode);
         $sTableName = MetaModel::DBGetTable($sOriginClass);
         $oAttDef = MetaModel::GetAttributeDef($sOriginClass, $sAttCode);
         if ($oAttDef instanceof AttributeEnum) {
             $oValDef = $oAttDef->GetValuesDef();
             if ($oValDef) {
                 $aNewValues = array_keys($oValDef->GetValues(array(), ""));
                 if (in_array($sTo, $aNewValues)) {
                     $sEnumCol = $oAttDef->Get("sql");
                     $aFields = CMDBSource::QueryToArray("SHOW COLUMNS FROM `{$sTableName}` WHERE Field = '{$sEnumCol}'");
                     if (isset($aFields[0]['Type'])) {
                         $sColType = $aFields[0]['Type'];
                         // Note: the parsing should rely on str_getcsv (requires PHP 5.3) to cope with escaped string
                         if (preg_match("/^enum\\(\\'(.*)\\'\\)\$/", $sColType, $aMatches)) {
                             $aCurrentValues = explode("','", $aMatches[1]);
                         }
                     }
                     if (!in_array($sFrom, $aNewValues)) {
                         if (!in_array($sTo, $aCurrentValues)) {
                             $sNullSpec = $oAttDef->IsNullAllowed() ? 'NULL' : 'NOT NULL';
                             if (strtolower($sTo) == strtolower($sFrom)) {
                                 SetupPage::log_info("Changing enum in DB - {$sClass}::{$sAttCode} from '{$sFrom}' to '{$sTo}' (just a change in the case)");
                                 $aTargetValues = array();
                                 foreach ($aCurrentValues as $sValue) {
                                     if ($sValue == $sFrom) {
                                         $sValue = $sTo;
                                     }
                                     $aTargetValues[] = $sValue;
                                 }
                                 $sColumnDefinition = "ENUM(" . implode(",", CMDBSource::Quote($aTargetValues)) . ") {$sNullSpec}";
                                 $sRepair = "ALTER TABLE `{$sTableName}` MODIFY `{$sEnumCol}` {$sColumnDefinition}";
                                 CMDBSource::Query($sRepair);
                             } else {
                                 // 1st - Allow both values in the column definition
                                 //
                                 SetupPage::log_info("Changing enum in DB - {$sClass}::{$sAttCode} from '{$sFrom}' to '{$sTo}'");
                                 $aAllValues = $aCurrentValues;
                                 $aAllValues[] = $sTo;
                                 $sColumnDefinition = "ENUM(" . implode(",", CMDBSource::Quote($aAllValues)) . ") {$sNullSpec}";
                                 $sRepair = "ALTER TABLE `{$sTableName}` MODIFY `{$sEnumCol}` {$sColumnDefinition}";
                                 CMDBSource::Query($sRepair);
                                 // 2nd - Change the old value into the new value
                                 //
                                 $sRepair = "UPDATE `{$sTableName}` SET `{$sEnumCol}` = '{$sTo}' WHERE `{$sEnumCol}` = BINARY '{$sFrom}'";
                                 CMDBSource::Query($sRepair);
                                 $iAffectedRows = CMDBSource::AffectedRows();
                                 SetupPage::log_info("Changing enum in DB - {$iAffectedRows} rows updated");
                                 // 3rd - Remove the useless value from the column definition
                                 //
                                 $aTargetValues = array();
                                 foreach ($aCurrentValues as $sValue) {
                                     if ($sValue == $sFrom) {
                                         $sValue = $sTo;
                                     }
                                     $aTargetValues[] = $sValue;
                                 }
                                 $sColumnDefinition = "ENUM(" . implode(",", CMDBSource::Quote($aTargetValues)) . ") {$sNullSpec}";
                                 $sRepair = "ALTER TABLE `{$sTableName}` MODIFY `{$sEnumCol}` {$sColumnDefinition}";
                                 CMDBSource::Query($sRepair);
                                 SetupPage::log_info("Changing enum in DB - removed useless value '{$sFrom}'");
                             }
                         }
                     } else {
                         SetupPage::log_warning("Changing enum in DB - {$sClass}::{$sAttCode} - '{$sFrom}' is still a valid value (" . implode(', ', $aNewValues) . ")");
                     }
                 } else {
                     SetupPage::log_warning("Changing enum in DB - {$sClass}::{$sAttCode} - '{$sTo}' is not a known value (" . implode(', ', $aNewValues) . ")");
                 }
             }
         }
     } catch (Exception $e) {
         SetupPage::log_warning("Changing enum in DB - {$sClass}::{$sAttCode} - '{$sTo}' failed. Reason " . $e->getMessage());
     }
 }
 /**
  * Helper to modify an enum value	
  * The change is made in the datamodel definition, but the value has to be changed in the DB as well	 	 
  * Must be called BEFORE DB update, i.e within an implementation of BeforeDatabaseCreation()
  * 	 
  * @param string $sClass A valid class name
  * @param string $sAttCode The enum attribute code
  * @param string $sFrom Original value (already INVALID in the current datamodel)	 	
  * @param string $sTo New value (valid in the current datamodel)
  * @return void	 	 	
  */
 public static function RenameEnumValueInDB($sClass, $sAttCode, $sFrom, $sTo)
 {
     $sOriginClass = MetaModel::GetAttributeOrigin($sClass, $sAttCode);
     $sTableName = MetaModel::DBGetTable($sOriginClass);
     $oAttDef = MetaModel::GetAttributeDef($sOriginClass, $sAttCode);
     if ($oAttDef instanceof AttributeEnum) {
         $oValDef = $oAttDef->GetValuesDef();
         if ($oValDef) {
             $aNewValues = array_keys($oValDef->GetValues(array(), ""));
             if (in_array($sTo, $aNewValues)) {
                 $aAllValues = $aNewValues;
                 $aAllValues[] = $sFrom;
                 if (!in_array($sFrom, $aNewValues)) {
                     $sEnumCol = $oAttDef->Get("sql");
                     $sNullSpec = $oAttDef->IsNullAllowed() ? 'NULL' : 'NOT NULL';
                     if (strtolower($sTo) == strtolower($sFrom)) {
                         SetupPage::log_info("Changing enum in DB - {$sClass}::{$sAttCode} from '{$sFrom}' to '{$sTo}' (just a change in the case)");
                         $sColumnDefinition = "ENUM(" . implode(",", CMDBSource::Quote($aNewValues)) . ") {$sNullSpec}";
                         $sRepair = "ALTER TABLE `{$sTableName}` MODIFY `{$sEnumCol}` {$sColumnDefinition}";
                         CMDBSource::Query($sRepair);
                     } else {
                         // 1st - Allow both values in the column definition
                         //
                         SetupPage::log_info("Changing enum in DB - {$sClass}::{$sAttCode} from '{$sFrom}' to '{$sTo}'");
                         $sColumnDefinition = "ENUM(" . implode(",", CMDBSource::Quote($aAllValues)) . ") {$sNullSpec}";
                         $sRepair = "ALTER TABLE `{$sTableName}` MODIFY `{$sEnumCol}` {$sColumnDefinition}";
                         CMDBSource::Query($sRepair);
                         // 2nd - Change the old value into the new value
                         //
                         $sRepair = "UPDATE `{$sTableName}` SET `{$sEnumCol}` = '{$sTo}' WHERE `{$sEnumCol}` = BINARY '{$sFrom}'";
                         CMDBSource::Query($sRepair);
                         $iAffectedRows = CMDBSource::AffectedRows();
                         SetupPage::log_info("Changing enum in DB - {$iAffectedRows} rows updated");
                         // 3rd - Remove the useless value from the column definition
                         //
                         $sColumnDefinition = "ENUM(" . implode(",", CMDBSource::Quote($aNewValues)) . ") {$sNullSpec}";
                         $sRepair = "ALTER TABLE `{$sTableName}` MODIFY `{$sEnumCol}` {$sColumnDefinition}";
                         CMDBSource::Query($sRepair);
                         SetupPage::log_info("Changing enum in DB - removed useless value '{$sFrom}'");
                     }
                 } else {
                     SetupPage::log_warning("Changing enum in DB - {$sClass}::{$sAttCode} - '{$sFrom}' is still a valid value (" . implode(', ', $aNewValues) . ")");
                 }
             } else {
                 SetupPage::log_warning("Changing enum in DB - {$sClass}::{$sAttCode} - '{$sTo}' is not a known value (" . implode(', ', $aNewValues) . ")");
             }
         }
     }
 }