mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
N°2490 Setup/toolkit : no longer generates useless ALTER TABLE queries on MariaDB >= 10.2
* case insensitive SQL data type comparison * some options have also case differences (example 'int(11) unsigned') * DEFAULT 'NULL' added by MariaDB on all nullable fields * default values are always surrounded with single quotes on MariaDB This is a Combodo implementation of PR #91
This commit is contained in:
@@ -589,6 +589,24 @@ class CMDBSource
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* MariaDB returns "'value'" for enum, while MySQL returns "value" (without the surrounding single quotes)
|
||||
*
|
||||
* @param string $sValue
|
||||
*
|
||||
* @return string without the surrounding quotes
|
||||
* @since 2.7.0 N°2490
|
||||
*/
|
||||
private static function RemoveSurroundingQuotes($sValue)
|
||||
{
|
||||
if (utils::StartsWith($sValue, '\'') && utils::EndsWith($sValue, '\''))
|
||||
{
|
||||
$sValue = substr($sValue, 1, -1);
|
||||
}
|
||||
|
||||
return $sValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sSQLQuery
|
||||
*
|
||||
@@ -1113,6 +1131,78 @@ class CMDBSource
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* There may have some differences between DB : for example in MySQL 5.7 we have "INT", while in MariaDB >= 10.2 you get "int DEFAULT 'NULL'"
|
||||
*
|
||||
* We still do a case sensitive comparison for enum values !
|
||||
*
|
||||
* A better solution would be to generate SQL field definitions ({@link GetFieldSpec} method) based on the DB used... But for
|
||||
* now (N°2490 / SF #1756 / PR #91) we did implement this simpler solution
|
||||
*
|
||||
* @param string $sItopGeneratedFieldType
|
||||
* @param string $sDbFieldType
|
||||
*
|
||||
* @return bool true if same type and options (case sensitive comparison only for type options), false otherwise
|
||||
* @since 2.7.0 N°2490
|
||||
*/
|
||||
public static function IsSameFieldTypes($sItopGeneratedFieldType, $sDbFieldType)
|
||||
{
|
||||
list($sItopFieldDataType, $sItopFieldTypeOptions, $sItopFieldOtherOptions) = static::GetFieldDataTypeAndOptions($sItopGeneratedFieldType);
|
||||
list($sDbFieldDataType, $sDbFieldTypeOptions, $sDbFieldOtherOptions) = static::GetFieldDataTypeAndOptions($sDbFieldType);
|
||||
|
||||
if (strcasecmp($sItopFieldDataType, $sDbFieldDataType) !== 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp($sItopFieldTypeOptions, $sDbFieldTypeOptions) !== 0)
|
||||
{
|
||||
// case sensitive comp as we need to check case for enum possible values for example
|
||||
return false;
|
||||
}
|
||||
|
||||
// remove the default value NULL added by MariadDB
|
||||
$sMariaDbDefaultNull = ' DEFAULT \'NULL\'';
|
||||
if (utils::EndsWith($sDbFieldOtherOptions, $sMariaDbDefaultNull))
|
||||
{
|
||||
$sDbFieldOtherOptions = substr($sDbFieldOtherOptions, 0, -strlen($sMariaDbDefaultNull));
|
||||
}
|
||||
// remove quotes around default values (always present in MariaDB)
|
||||
if (preg_match('/ DEFAULT \'([^\']+)\'$/', $sDbFieldOtherOptions, $aMatches) === 1)
|
||||
{
|
||||
if (($sItopFieldDataType !== 'ENUM') && is_numeric($aMatches[1]))
|
||||
{
|
||||
$sDbFieldOtherOptions = substr($sDbFieldOtherOptions, 0, -(strlen($aMatches[1]) + 2))
|
||||
.$aMatches[1];
|
||||
}
|
||||
}
|
||||
if (strcasecmp($sItopFieldOtherOptions, $sDbFieldOtherOptions) !== 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sCompleteFieldType sql field type, for example 'VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT 0'
|
||||
*
|
||||
* @return string[] consisting of 3 items :
|
||||
* 1. data type : for example 'VARCHAR'
|
||||
* 2. type value : for example '255'
|
||||
* 3. other options : for example ' CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT 0'
|
||||
*/
|
||||
private static function GetFieldDataTypeAndOptions($sCompleteFieldType)
|
||||
{
|
||||
preg_match('/^([a-zA-Z]+)(\(([^\)]+)\))?( .+)?$/', $sCompleteFieldType, $aMatches);
|
||||
|
||||
$sDataType = $aMatches[1];
|
||||
$sTypeOptions = isset($aMatches[2]) ? $aMatches[3] : '';
|
||||
$sOtherOptions = isset($aMatches[4]) ? $aMatches[4] : '';
|
||||
|
||||
return array($sDataType, $sTypeOptions, $sOtherOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sTable
|
||||
* @param string $sField
|
||||
@@ -1164,7 +1254,8 @@ class CMDBSource
|
||||
}
|
||||
elseif (is_string($aFieldData["Default"]) == 'string')
|
||||
{
|
||||
$sRet .= ' DEFAULT '.self::Quote($aFieldData["Default"]);
|
||||
$sDefaultValue = static::RemoveSurroundingQuotes($aFieldData["Default"]);
|
||||
$sRet .= ' DEFAULT '.self::Quote($sDefaultValue);
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
|
||||
@@ -5514,7 +5514,7 @@ abstract class MetaModel
|
||||
//
|
||||
$bToBeChanged = false;
|
||||
$sActualFieldSpec = CMDBSource::GetFieldSpec($sTable, $sField);
|
||||
if (strcasecmp($sDBFieldSpec, $sActualFieldSpec) != 0)
|
||||
if (!CMDBSource::IsSameFieldTypes($sDBFieldSpec, $sActualFieldSpec))
|
||||
{
|
||||
$bToBeChanged = true;
|
||||
$aErrors[$sClass][$sAttCode][] = "field '$sField' in table '$sTable' has a wrong type: found '$sActualFieldSpec' while expecting '$sDBFieldSpec'";
|
||||
|
||||
101
test/core/CMDBSourceTest.php
Normal file
101
test/core/CMDBSourceTest.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Core;
|
||||
|
||||
use CMDBSource;
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
|
||||
/**
|
||||
* @since 2.7.0
|
||||
*
|
||||
* @package Combodo\iTop\Test\UnitTest\Core
|
||||
*/
|
||||
|
||||
/**
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
*/
|
||||
class CMDBSourceTest extends ItopTestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
require_once(APPROOT.'/core/cmdbsource.class.inc.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers CMDBSource::IsSameFieldTypes
|
||||
* @dataProvider compareFieldTypesProvider
|
||||
*
|
||||
* @param boolean $bResult
|
||||
* @param string $sItopFieldType
|
||||
* @param string $sDbFieldType
|
||||
*/
|
||||
public function testCompareFieldTypes($bResult, $sItopFieldType, $sDbFieldType)
|
||||
{
|
||||
$this->assertEquals($bResult, CMDBSource::IsSameFieldTypes($sItopFieldType, $sDbFieldType));
|
||||
}
|
||||
|
||||
public function compareFieldTypesProvider()
|
||||
{
|
||||
return array(
|
||||
'same datetime types' => array(true, 'DATETIME', 'DATETIME'),
|
||||
'different types' => array(false, 'VARCHAR(255)', 'INT(11)'),
|
||||
'different types, same type options' => array(false, 'VARCHAR(11)', 'INT(11)'),
|
||||
'same int declaration, same case' => array(true, 'INT(11)', 'INT(11)'),
|
||||
'same int declaration, different case on data type' => array(true, 'INT(11)', 'int(11)'),
|
||||
'same enum declaration, same case' => array(
|
||||
true,
|
||||
'ENUM(\'error\',\'idle\',\'planned\',\'running\')',
|
||||
'ENUM(\'error\',\'idle\',\'planned\',\'running\')',
|
||||
),
|
||||
'same enum declaration, different case on data type' => array(
|
||||
true,
|
||||
'ENUM(\'error\',\'idle\',\'planned\',\'running\')',
|
||||
'enum(\'error\',\'idle\',\'planned\',\'running\')',
|
||||
),
|
||||
'same enum declaration, different case on type options' => array(
|
||||
false,
|
||||
'ENUM(\'ERROR\',\'IDLE\',\'planned\',\'running\')',
|
||||
'ENUM(\'error\',\'idle\',\'planned\',\'running\')',
|
||||
),
|
||||
'same enum declaration, different case on both data type and type options' => array(
|
||||
false,
|
||||
'ENUM(\'ERROR\',\'IDLE\',\'planned\',\'running\')',
|
||||
'enum(\'error\',\'idle\',\'planned\',\'running\')',
|
||||
),
|
||||
'MariaDB 10.2 nullable datetime' => array(
|
||||
true,
|
||||
'DATETIME',
|
||||
'datetime DEFAULT \'NULL\'',
|
||||
),
|
||||
'MariaDB 10.2 nullable text' => array(
|
||||
true,
|
||||
'TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci',
|
||||
'text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT \'NULL\'',
|
||||
),
|
||||
'MariaDB 10.2 nullable unsigned int' => array(
|
||||
true,
|
||||
'INT(11) UNSIGNED',
|
||||
'int(11) unsigned DEFAULT \'NULL\'',
|
||||
),
|
||||
'MariaDB 10.2 varchar with default value' => array(
|
||||
true,
|
||||
'VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT 0',
|
||||
'varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT \'0\'',
|
||||
),
|
||||
'MariaDB 10.2 Enum with string default value' => array(
|
||||
true,
|
||||
'ENUM(\'error\',\'idle\',\'planned\',\'running\') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT \'planned\'',
|
||||
'enum(\'error\',\'idle\',\'planned\',\'running\') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT \'planned\'',
|
||||
),
|
||||
'MariaDB 10.2 Enum with numeric default value' => array(
|
||||
true,
|
||||
'ENUM(\'1\',\'2\',\'3\') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT \'1\'',
|
||||
'enum(\'1\',\'2\',\'3\') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT \'1\'',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user