From 6acf0901aca251a9710e89fa4c640b62ea0d5988 Mon Sep 17 00:00:00 2001 From: Mo Sureerat Date: Mon, 10 Oct 2022 22:39:10 +0700 Subject: [PATCH 01/16] Support UUID for MariaDB Signed-off-by: Mo Sureerat --- libraries/classes/Types.php | 41 + test/classes/TypesByDatabaseVersionTest.php | 1035 +++++++++++++++++++ test/classes/TypesTest.php | 137 ++- 3 files changed, 1142 insertions(+), 71 deletions(-) create mode 100644 test/classes/TypesByDatabaseVersionTest.php diff --git a/libraries/classes/Types.php b/libraries/classes/Types.php index 4c2b2eeb78..47c7230e14 100644 --- a/libraries/classes/Types.php +++ b/libraries/classes/Types.php @@ -25,6 +25,8 @@ use function strncasecmp; */ class Types { + private const UUID_SUPPORT_MARIADB_VERSION = 100700; // 10.7.0 + /** @var DatabaseInterface Database interface */ private $dbi; @@ -418,6 +420,9 @@ class Types return __('Intended for storage of IPv6 addresses, as well as IPv4 ' . 'addresses assuming conventional mapping of IPv4 addresses ' . 'into IPv6 addresses'); + + case 'UUID': + return __('128-bit UUID (Universally Unique Identifier)'); } return ''; @@ -485,6 +490,9 @@ class Types case 'JSON': return 'JSON'; + + case 'UUID': + return 'UUID'; } return ''; @@ -545,6 +553,10 @@ class Types $ret = array_diff($ret, ['INET6_NTOA']); } + if (! $this->isUUIDEnabled()) { + $ret = array_diff($ret, ['UUID']); + } + return $ret; case 'DATE': @@ -621,10 +633,15 @@ class Types 'WEEKOFYEAR', 'YEARWEEK', ]; + if (($isMariaDB && $serverVersion < 100012) || $serverVersion < 50603) { $ret = array_diff($ret, ['INET6_ATON']); } + if (! $this->isUUIDEnabled()) { + $ret = array_diff($ret, ['UUID_SHORT']); + } + return $ret; case 'SPATIAL': @@ -752,6 +769,11 @@ class Types 'TEXT', 'DATE', ]; + + if ($this->isUUIDEnabled()) { + $ret[] = 'UUID'; + } + // numeric $ret[_pgettext('numeric types', 'Numeric')] = [ 'TINYINT', @@ -822,6 +844,10 @@ class Types $ret['JSON'] = ['JSON']; } + if ($this->isUUIDEnabled()) { + $ret['UUID'] = ['UUID']; + } + return $ret; } @@ -906,4 +932,19 @@ class Types '', ]; } + + /** + * Check whether the database support uuid + * true if uuid is supported + * + * @return bool enabled status + */ + private function isUUIDEnabled(): bool + { + if (! $this->dbi->isMariaDB()) { + return false; + } + + return $this->dbi->getVersion() >= self::UUID_SUPPORT_MARIADB_VERSION; + } } diff --git a/test/classes/TypesByDatabaseVersionTest.php b/test/classes/TypesByDatabaseVersionTest.php new file mode 100644 index 0000000000..89fba14be6 --- /dev/null +++ b/test/classes/TypesByDatabaseVersionTest.php @@ -0,0 +1,1035 @@ +dbiStub = $this->createStub(DatabaseInterface::class); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown(): void + { + parent::tearDown(); + + unset($this->dbiStub); + unset($this->object); + } + + /** + * @param string $database Database + * @param int $dbVersion Database Version + * @param string $class The class to get function list. + * @param array $includes Expected elements should contain in result + * @param array $excludes Expected elements should not contain in result + * @phpstan-param array $includes + * @phpstan-param array $excludes + * + * @dataProvider providerFortTestGetFunctionsClass + */ + public function testGetFunctionsClass( + string $database, + int $dbVersion, + string $class, + array $includes, + array $excludes + ): void { + $this->createObject($database, $dbVersion); + + $result = $this->object->getFunctionsClass($class); + + if ($includes) { + foreach ($includes as $value) { + $this->assertContains($value, $result); + } + } + + if (! $excludes) { + return; + } + + foreach ($excludes as $value) { + $this->assertNotContains($value, $result); + } + } + + /** + * Data provider for testing function lists + * + * @psalm-return array, array}> + */ + public function providerFortTestGetFunctionsClass(): array + { + return [ + 'mysql 5.1.0 - CHAR - not support INET6 Converter and UUID' => [ + 'mysql', + 50100, + 'CHAR', + // should contains + [], + // should not existed + [ 'INET6_NTOA', 'UUID' ], + ], + 'mysql 8.0.30 - CHAR - support INET6 Converter but not support UUID' => [ + 'mysql', + 80030, + 'CHAR', + // should contains + [ 'INET6_NTOA' ], + // should not existed + [ 'UUID' ], + ], + 'mariadb 5.1.0 - CHAR - not support INET6 Converter and UUID' => [ + 'mariadb', + 50100, + 'CHAR', + // should contains + [], + // should not existed + [ 'INET6_NTOA', 'UUID' ], + ], + 'mariadb 10.0.12 - CHAR - support INET6 Converter but not support UUID' => [ + 'mariadb', + 100012, + 'CHAR', + // should contains + [ 'INET6_NTOA' ], + // should not existed + [ 'UUID' ], + ], + 'mariadb 10.9.3 - CHAR - support INET6 Converter and UUID' => [ + 'mariadb', + 100903, + 'CHAR', + // should contains + [ 'INET6_NTOA', 'UUID' ], + // should not existed + [], + ], + 'mysql 5.1.0 - NUMBER - not support INET6 Converter and UUID' => [ + 'mysql', + 50100, + 'NUMBER', + // should contains + [], + // should not existed + [ 'INET6_ATON', 'UUID_SHORT' ], + ], + 'mysql 8.0.30 - NUMBER - support INET6 Converter but not support UUID' => [ + 'mysql', + 80030, + 'NUMBER', + // should contains + [ 'INET6_ATON' ], + // should not existed + [ 'UUID_SHORT' ], + ], + 'mariadb 5.1.0 - NUMBER - not support INET6 Converter and UUID' => [ + 'mariadb', + 50100, + 'NUMBER', + // should contains + [], + // should not existed + [ 'INET6_ATON', 'UUID_SHORT' ], + ], + 'mariadb 10.0.12 - NUMBER - support INET6 Converter but not support UUID' => [ + 'mariadb', + 100012, + 'NUMBER', + // should contains + [ 'INET6_ATON' ], + // should not existed + [ 'UUID_SHORT' ], + ], + 'mariadb 10.9.3 - NUMBER - support INET6 Converter and UUID' => [ + 'mariadb', + 100903, + 'NUMBER', + // should contains + [ 'INET6_ATON', 'UUID_SHORT' ], + // should not existed + [], + ], + 'mysql 5.1.0 - SPATIAL - not support ST_Geometry' => [ + 'mysql', + 50100, + 'SPATIAL', + // should contains + [ + 'GeomFromText', + 'GeomFromWKB', + 'GeomCollFromText', + 'LineFromText', + 'MLineFromText', + 'PointFromText', + 'MPointFromText', + 'PolyFromText', + 'MPolyFromText', + 'GeomCollFromWKB', + 'LineFromWKB', + 'MLineFromWKB', + 'PointFromWKB', + 'MPointFromWKB', + 'PolyFromWKB', + 'MPolyFromWKB', + ], + // should not existed + [ + 'ST_GeomFromText', + 'ST_GeomFromWKB', + 'ST_GeomCollFromText', + 'ST_LineFromText', + 'ST_MLineFromText', + 'ST_PointFromText', + 'ST_MPointFromText', + 'ST_PolyFromText', + 'ST_MPolyFromText', + 'ST_GeomCollFromWKB', + 'ST_LineFromWKB', + 'ST_MLineFromWKB', + 'ST_PointFromWKB', + 'ST_MPointFromWKB', + 'ST_PolyFromWKB', + 'ST_MPolyFromWKB', + ], + ], + 'mysql 8.0.30 - SPATIAL - support ST_Geometry' => [ + 'mysql', + 80030, + 'SPATIAL', + // should contains + [ + 'ST_GeomFromText', + 'ST_GeomFromWKB', + 'ST_GeomCollFromText', + 'ST_LineFromText', + 'ST_MLineFromText', + 'ST_PointFromText', + 'ST_MPointFromText', + 'ST_PolyFromText', + 'ST_MPolyFromText', + 'ST_GeomCollFromWKB', + 'ST_LineFromWKB', + 'ST_MLineFromWKB', + 'ST_PointFromWKB', + 'ST_MPointFromWKB', + 'ST_PolyFromWKB', + 'ST_MPolyFromWKB', + ], + // should not existed + [ + 'GeomFromText', + 'GeomFromWKB', + 'GeomCollFromText', + 'LineFromText', + 'MLineFromText', + 'PointFromText', + 'MPointFromText', + 'PolyFromText', + 'MPolyFromText', + 'GeomCollFromWKB', + 'LineFromWKB', + 'MLineFromWKB', + 'PointFromWKB', + 'MPointFromWKB', + 'PolyFromWKB', + 'MPolyFromWKB', + ], + ], + ]; + } + + /** + * Test for getFunctions + * + * @param string $database Database + * @param int $dbVersion Database Version + * @param array $includes Expected elements should contain in result + * @param array $excludes Expected elements should not contain in result + * @phpstan-param array $includes + * @phpstan-param array $excludes + * + * @dataProvider providerFortTestGetFunctions + */ + public function testGetFunctions(string $database, int $dbVersion, array $includes, array $excludes): void + { + $this->createObject($database, $dbVersion); + + $result = $this->object->getFunctions('enum'); + + if ($includes) { + foreach ($includes as $value) { + $this->assertContains($value, $result); + } + } + + if (! $excludes) { + return; + } + + foreach ($excludes as $value) { + $this->assertNotContains($value, $result); + } + } + + /** + * Data provider for testing get functions + * + * @psalm-return array, array}> + */ + public function providerFortTestGetFunctions(): array + { + return [ + 'mysql 5.1.0 - not support INET6 Converter' => [ + 'mysql', + 50100, + // should contains + [], + // should not existed + [ 'INET6_NTOA' ], + ], + 'mysql 8.0.30 - support INET6 Converter' => [ + 'mysql', + 80030, + // should contains + [ 'INET6_NTOA' ], + // should not existed + [], + ], + 'mariadb 5.1.0 - not support INET6 Converter' => [ + 'mariadb', + 50100, + // should contains + [], + // should not existed + [ 'INET6_NTOA' ], + ], + 'mariadb 10.9.3 - support INET6 Converter' => [ + 'mariadb', + 100903, + // should contains + [ 'INET6_NTOA' ], + // should not existed + [], + ], + ]; + } + + /** + * Test for getAllFunctions + * + * @param string $database Database + * @param int $dbVersion Database Version + * @param array $includes Expected elements should contain in result + * @param array $excludes Expected elements should not contain in result + * @phpstan-param array $includes + * @phpstan-param array $excludes + * + * @dataProvider providerFortTestGetAllFunctions + */ + public function testGetAllFunctions(string $database, int $dbVersion, array $includes, array $excludes): void + { + $this->createObject($database, $dbVersion); + + $result = $this->object->getAllFunctions(); + + if ($includes) { + foreach ($includes as $value) { + $this->assertContains($value, $result); + } + } + + if (! $excludes) { + return; + } + + foreach ($excludes as $value) { + $this->assertNotContains($value, $result); + } + } + + /** + * Data provider for testing get all functions + * + * @psalm-return array, array}> + */ + public function providerFortTestGetAllFunctions(): array + { + return [ + 'mysql 5.1.0 - not support INET6_ATON, ST_Geometry and UUID' => [ + 'mysql', + 50100, + [ + 'GeomFromText', + 'GeomFromWKB', + 'GeomCollFromText', + 'LineFromText', + 'MLineFromText', + 'PointFromText', + 'MPointFromText', + 'PolyFromText', + 'MPolyFromText', + 'GeomCollFromWKB', + 'LineFromWKB', + 'MLineFromWKB', + 'PointFromWKB', + 'MPointFromWKB', + 'PolyFromWKB', + 'MPolyFromWKB', + ], + [ + 'INET6_ATON', + 'INET6_ATON', + 'ST_GeomFromText', + 'ST_GeomFromWKB', + 'ST_GeomCollFromText', + 'ST_LineFromText', + 'ST_MLineFromText', + 'ST_PointFromText', + 'ST_MPointFromText', + 'ST_PolyFromText', + 'ST_MPolyFromText', + 'ST_GeomCollFromWKB', + 'ST_LineFromWKB', + 'ST_MLineFromWKB', + 'ST_PointFromWKB', + 'ST_MPointFromWKB', + 'ST_PolyFromWKB', + 'ST_MPolyFromWKB', + 'UUID', + 'UUID_SHORT', + ], + ], + 'mysql 8.0.30 - support INET6_ATON and ST_Geometry' => [ + 'mysql', + 80030, + [ + 'INET6_ATON', + 'INET6_ATON', + 'ST_GeomFromText', + 'ST_GeomFromWKB', + 'ST_GeomCollFromText', + 'ST_LineFromText', + 'ST_MLineFromText', + 'ST_PointFromText', + 'ST_MPointFromText', + 'ST_PolyFromText', + 'ST_MPolyFromText', + 'ST_GeomCollFromWKB', + 'ST_LineFromWKB', + 'ST_MLineFromWKB', + 'ST_PointFromWKB', + 'ST_MPointFromWKB', + 'ST_PolyFromWKB', + 'ST_MPolyFromWKB', + ], + [ + 'GeomFromText', + 'GeomFromWKB', + 'GeomCollFromText', + 'LineFromText', + 'MLineFromText', + 'PointFromText', + 'MPointFromText', + 'PolyFromText', + 'MPolyFromText', + 'GeomCollFromWKB', + 'LineFromWKB', + 'MLineFromWKB', + 'PointFromWKB', + 'MPointFromWKB', + 'PolyFromWKB', + 'MPolyFromWKB', + 'UUID', + 'UUID_SHORT', + ], + ], + 'mariadb 5.1.0 - not support INET6_ATON and ST_Geometry' => [ + 'mariadb', + 50100, + [ + 'GeomFromText', + 'GeomFromWKB', + 'GeomCollFromText', + 'LineFromText', + 'MLineFromText', + 'PointFromText', + 'MPointFromText', + 'PolyFromText', + 'MPolyFromText', + 'GeomCollFromWKB', + 'LineFromWKB', + 'MLineFromWKB', + 'PointFromWKB', + 'MPointFromWKB', + 'PolyFromWKB', + 'MPolyFromWKB', + ], + [ + 'INET6_ATON', + 'INET6_ATON', + 'ST_GeomFromText', + 'ST_GeomFromWKB', + 'ST_GeomCollFromText', + 'ST_LineFromText', + 'ST_MLineFromText', + 'ST_PointFromText', + 'ST_MPointFromText', + 'ST_PolyFromText', + 'ST_MPolyFromText', + 'ST_GeomCollFromWKB', + 'ST_LineFromWKB', + 'ST_MLineFromWKB', + 'ST_PointFromWKB', + 'ST_MPointFromWKB', + 'ST_PolyFromWKB', + 'ST_MPolyFromWKB', + 'UUID', + 'UUID_SHORT', + ], + ], + 'mariadb 10.6.0 - support INET6_ATON and ST_Geometry' => [ + 'mariadb', + 100600, + [ + 'INET6_ATON', + 'INET6_ATON', + 'ST_GeomFromText', + 'ST_GeomFromWKB', + 'ST_GeomCollFromText', + 'ST_LineFromText', + 'ST_MLineFromText', + 'ST_PointFromText', + 'ST_MPointFromText', + 'ST_PolyFromText', + 'ST_MPolyFromText', + 'ST_GeomCollFromWKB', + 'ST_LineFromWKB', + 'ST_MLineFromWKB', + 'ST_PointFromWKB', + 'ST_MPointFromWKB', + 'ST_PolyFromWKB', + 'ST_MPolyFromWKB', + ], + [ + 'GeomFromText', + 'GeomFromWKB', + 'GeomCollFromText', + 'LineFromText', + 'MLineFromText', + 'PointFromText', + 'MPointFromText', + 'PolyFromText', + 'MPolyFromText', + 'GeomCollFromWKB', + 'LineFromWKB', + 'MLineFromWKB', + 'PointFromWKB', + 'MPointFromWKB', + 'PolyFromWKB', + 'MPolyFromWKB', + 'UUID', + 'UUID_SHORT', + ], + ], + 'mariadb 10.9.3 - support INET6_ATON, ST_Geometry and UUID' => [ + 'mariadb', + 100903, + [ + 'INET6_ATON', + 'INET6_ATON', + 'ST_GeomFromText', + 'ST_GeomFromWKB', + 'ST_GeomCollFromText', + 'ST_LineFromText', + 'ST_MLineFromText', + 'ST_PointFromText', + 'ST_MPointFromText', + 'ST_PolyFromText', + 'ST_MPolyFromText', + 'ST_GeomCollFromWKB', + 'ST_LineFromWKB', + 'ST_MLineFromWKB', + 'ST_PointFromWKB', + 'ST_MPointFromWKB', + 'ST_PolyFromWKB', + 'ST_MPolyFromWKB', + 'UUID', + 'UUID_SHORT', + ], + [ + 'GeomFromText', + 'GeomFromWKB', + 'GeomCollFromText', + 'LineFromText', + 'MLineFromText', + 'PointFromText', + 'MPointFromText', + 'PolyFromText', + 'MPolyFromText', + 'GeomCollFromWKB', + 'LineFromWKB', + 'MLineFromWKB', + 'PointFromWKB', + 'MPointFromWKB', + 'PolyFromWKB', + 'MPolyFromWKB', + ], + ], + ]; + } + + /** + * Test for getColumns + * + * @param string $database Database + * @param int $dbVersion Database Version + * @param array $expected Expected Result + * @phpstan-param array|string> $expected + * + * @dataProvider providerFortTestGetColumns + */ + public function testGetColumns(string $database, int $dbVersion, array $expected): void + { + $this->createObject($database, $dbVersion); + + $this->assertEquals($expected, $this->object->getColumns()); + } + + /** + * Data provider for testing test columns + * + * @psalm-return array|string>}> + */ + public function providerFortTestGetColumns(): array + { + return [ + 'mysql 5.1.0 - not support INET6, JSON and UUID' => [ + 'mysql', + 50100, + [ + 0 => 'INT', + 1 => 'VARCHAR', + 2 => 'TEXT', + 3 => 'DATE', + 'Numeric' => [ + 'TINYINT', + 'SMALLINT', + 'MEDIUMINT', + 'INT', + 'BIGINT', + '-', + 'DECIMAL', + 'FLOAT', + 'DOUBLE', + 'REAL', + '-', + 'BIT', + 'BOOLEAN', + 'SERIAL', + ], + 'Date and time' => [ + 'DATE', + 'DATETIME', + 'TIMESTAMP', + 'TIME', + 'YEAR', + ], + 'String' => [ + 'CHAR', + 'VARCHAR', + '-', + 'TINYTEXT', + 'TEXT', + 'MEDIUMTEXT', + 'LONGTEXT', + '-', + 'BINARY', + 'VARBINARY', + '-', + 'TINYBLOB', + 'BLOB', + 'MEDIUMBLOB', + 'LONGBLOB', + '-', + 'ENUM', + 'SET', + ], + 'Spatial' => [ + 'GEOMETRY', + 'POINT', + 'LINESTRING', + 'POLYGON', + 'MULTIPOINT', + 'MULTILINESTRING', + 'MULTIPOLYGON', + 'GEOMETRYCOLLECTION', + ], + ], + ], + 'mysql 8.0.30 - support JSON but not support INET6 and UUID' => [ + 'mysql', + 80030, + [ + 0 => 'INT', + 1 => 'VARCHAR', + 2 => 'TEXT', + 3 => 'DATE', + 'Numeric' => [ + 'TINYINT', + 'SMALLINT', + 'MEDIUMINT', + 'INT', + 'BIGINT', + '-', + 'DECIMAL', + 'FLOAT', + 'DOUBLE', + 'REAL', + '-', + 'BIT', + 'BOOLEAN', + 'SERIAL', + ], + 'Date and time' => [ + 'DATE', + 'DATETIME', + 'TIMESTAMP', + 'TIME', + 'YEAR', + ], + 'String' => [ + 'CHAR', + 'VARCHAR', + '-', + 'TINYTEXT', + 'TEXT', + 'MEDIUMTEXT', + 'LONGTEXT', + '-', + 'BINARY', + 'VARBINARY', + '-', + 'TINYBLOB', + 'BLOB', + 'MEDIUMBLOB', + 'LONGBLOB', + '-', + 'ENUM', + 'SET', + ], + 'Spatial' => [ + 'GEOMETRY', + 'POINT', + 'LINESTRING', + 'POLYGON', + 'MULTIPOINT', + 'MULTILINESTRING', + 'MULTIPOLYGON', + 'GEOMETRYCOLLECTION', + ], + 'JSON' => ['JSON'], + ], + ], + 'mariadb 5.1.0 - not support INET6, JSON and UUID' => [ + 'mariadb', + 50100, + [ + 0 => 'INT', + 1 => 'VARCHAR', + 2 => 'TEXT', + 3 => 'DATE', + 'Numeric' => [ + 'TINYINT', + 'SMALLINT', + 'MEDIUMINT', + 'INT', + 'BIGINT', + '-', + 'DECIMAL', + 'FLOAT', + 'DOUBLE', + 'REAL', + '-', + 'BIT', + 'BOOLEAN', + 'SERIAL', + ], + 'Date and time' => [ + 'DATE', + 'DATETIME', + 'TIMESTAMP', + 'TIME', + 'YEAR', + ], + 'String' => [ + 'CHAR', + 'VARCHAR', + '-', + 'TINYTEXT', + 'TEXT', + 'MEDIUMTEXT', + 'LONGTEXT', + '-', + 'BINARY', + 'VARBINARY', + '-', + 'TINYBLOB', + 'BLOB', + 'MEDIUMBLOB', + 'LONGBLOB', + '-', + 'ENUM', + 'SET', + ], + 'Spatial' => [ + 'GEOMETRY', + 'POINT', + 'LINESTRING', + 'POLYGON', + 'MULTIPOINT', + 'MULTILINESTRING', + 'MULTIPOLYGON', + 'GEOMETRYCOLLECTION', + ], + ], + ], + 'mariadb 10.2.8 - support JSON but not support INET6 and UUID' => [ + 'mariadb', + 100208, + [ + 0 => 'INT', + 1 => 'VARCHAR', + 2 => 'TEXT', + 3 => 'DATE', + 'Numeric' => [ + 'TINYINT', + 'SMALLINT', + 'MEDIUMINT', + 'INT', + 'BIGINT', + '-', + 'DECIMAL', + 'FLOAT', + 'DOUBLE', + 'REAL', + '-', + 'BIT', + 'BOOLEAN', + 'SERIAL', + ], + 'Date and time' => [ + 'DATE', + 'DATETIME', + 'TIMESTAMP', + 'TIME', + 'YEAR', + ], + 'String' => [ + 'CHAR', + 'VARCHAR', + '-', + 'TINYTEXT', + 'TEXT', + 'MEDIUMTEXT', + 'LONGTEXT', + '-', + 'BINARY', + 'VARBINARY', + '-', + 'TINYBLOB', + 'BLOB', + 'MEDIUMBLOB', + 'LONGBLOB', + '-', + 'ENUM', + 'SET', + ], + 'Spatial' => [ + 'GEOMETRY', + 'POINT', + 'LINESTRING', + 'POLYGON', + 'MULTIPOINT', + 'MULTILINESTRING', + 'MULTIPOLYGON', + 'GEOMETRYCOLLECTION', + ], + 'JSON' => [ 'JSON' ], + ], + ], + 'mariadb 10.5.0 - support JSON and INET6 but not support UUID' => [ + 'mariadb', + 100500, + [ + 0 => 'INT', + 1 => 'VARCHAR', + 2 => 'TEXT', + 3 => 'DATE', + 'Numeric' => [ + 'TINYINT', + 'SMALLINT', + 'MEDIUMINT', + 'INT', + 'BIGINT', + '-', + 'DECIMAL', + 'FLOAT', + 'DOUBLE', + 'REAL', + '-', + 'BIT', + 'BOOLEAN', + 'SERIAL', + ], + 'Date and time' => [ + 'DATE', + 'DATETIME', + 'TIMESTAMP', + 'TIME', + 'YEAR', + ], + 'String' => [ + 'CHAR', + 'VARCHAR', + '-', + 'TINYTEXT', + 'TEXT', + 'MEDIUMTEXT', + 'LONGTEXT', + '-', + 'BINARY', + 'VARBINARY', + '-', + 'TINYBLOB', + 'BLOB', + 'MEDIUMBLOB', + 'LONGBLOB', + '-', + 'ENUM', + 'SET', + '-', + 'INET6', + ], + 'Spatial' => [ + 'GEOMETRY', + 'POINT', + 'LINESTRING', + 'POLYGON', + 'MULTIPOINT', + 'MULTILINESTRING', + 'MULTIPOLYGON', + 'GEOMETRYCOLLECTION', + ], + 'JSON' => [ 'JSON' ], + ], + ], + 'mariadb 10.9.3 - support INET6, JSON and UUID' => [ + 'mariadb', + 100903, + [ + 0 => 'INT', + 1 => 'VARCHAR', + 2 => 'TEXT', + 3 => 'DATE', + 4 => 'UUID', + 'Numeric' => [ + 'TINYINT', + 'SMALLINT', + 'MEDIUMINT', + 'INT', + 'BIGINT', + '-', + 'DECIMAL', + 'FLOAT', + 'DOUBLE', + 'REAL', + '-', + 'BIT', + 'BOOLEAN', + 'SERIAL', + ], + 'Date and time' => [ + 'DATE', + 'DATETIME', + 'TIMESTAMP', + 'TIME', + 'YEAR', + ], + 'String' => [ + 'CHAR', + 'VARCHAR', + '-', + 'TINYTEXT', + 'TEXT', + 'MEDIUMTEXT', + 'LONGTEXT', + '-', + 'BINARY', + 'VARBINARY', + '-', + 'TINYBLOB', + 'BLOB', + 'MEDIUMBLOB', + 'LONGBLOB', + '-', + 'ENUM', + 'SET', + '-', + 'INET6', + ], + 'Spatial' => [ + 'GEOMETRY', + 'POINT', + 'LINESTRING', + 'POLYGON', + 'MULTIPOINT', + 'MULTILINESTRING', + 'MULTIPOLYGON', + 'GEOMETRYCOLLECTION', + ], + 'JSON' => [ 'JSON' ], + 'UUID' => [ 'UUID' ], + ], + ], + ]; + } + + /** + * @param string $database Database + * @param int $version Database Version + */ + private function createObject(string $database, int $version): void + { + $this->dbiStub->method('isMariaDB')->willReturn($database === 'mariadb'); + $this->dbiStub->method('getVersion')->willReturn($version); + $this->object = new Types($this->dbiStub); + } +} diff --git a/test/classes/TypesTest.php b/test/classes/TypesTest.php index d69b102e05..479ef61236 100644 --- a/test/classes/TypesTest.php +++ b/test/classes/TypesTest.php @@ -335,40 +335,39 @@ class TypesTest extends AbstractTestCase [ 'CHAR', [ - 'AES_DECRYPT', - 'AES_ENCRYPT', - 'BIN', - 'CHAR', - 'COMPRESS', - 'CURRENT_USER', - 'DATABASE', - 'DAYNAME', - 'DES_DECRYPT', - 'DES_ENCRYPT', - 'ENCRYPT', - 'HEX', - 'INET6_NTOA', - 'INET_NTOA', - 'LOAD_FILE', - 'LOWER', - 'LTRIM', - 'MD5', - 'MONTHNAME', - 'OLD_PASSWORD', - 'PASSWORD', - 'QUOTE', - 'REVERSE', - 'RTRIM', - 'SHA1', - 'SOUNDEX', - 'SPACE', - 'TRIM', - 'UNCOMPRESS', - 'UNHEX', - 'UPPER', - 'USER', - 'UUID', - 'VERSION', + '0' => 'AES_DECRYPT', + '1' => 'AES_ENCRYPT', + '2' => 'BIN', + '3' => 'CHAR', + '4' => 'COMPRESS', + '5' => 'CURRENT_USER', + '6' => 'DATABASE', + '7' => 'DAYNAME', + '8' => 'DES_DECRYPT', + '9' => 'DES_ENCRYPT', + '10' => 'ENCRYPT', + '11' => 'HEX', + '12' => 'INET6_NTOA', + '13' => 'INET_NTOA', + '14' => 'LOAD_FILE', + '15' => 'LOWER', + '16' => 'LTRIM', + '17' => 'MD5', + '18' => 'MONTHNAME', + '19' => 'OLD_PASSWORD', + '20' => 'PASSWORD', + '21' => 'QUOTE', + '22' => 'REVERSE', + '23' => 'RTRIM', + '24' => 'SHA1', + '25' => 'SOUNDEX', + '26' => 'SPACE', + '27' => 'TRIM', + '28' => 'UNCOMPRESS', + '29' => 'UNHEX', + '30' => 'UPPER', + '31' => 'USER', + '33' => 'VERSION', ], ], [ @@ -464,7 +463,6 @@ class TypesTest extends AbstractTestCase '44' => 'TIME_TO_SEC', '45' => 'UNCOMPRESSED_LENGTH', '46' => 'UNIX_TIMESTAMP', - '47' => 'UUID_SHORT', '48' => 'WEEK', '49' => 'WEEKDAY', '50' => 'WEEKOFYEAR', @@ -485,40 +483,39 @@ class TypesTest extends AbstractTestCase { $this->assertEquals( [ - 'AES_DECRYPT', - 'AES_ENCRYPT', - 'BIN', - 'CHAR', - 'COMPRESS', - 'CURRENT_USER', - 'DATABASE', - 'DAYNAME', - 'DES_DECRYPT', - 'DES_ENCRYPT', - 'ENCRYPT', - 'HEX', - 'INET6_NTOA', - 'INET_NTOA', - 'LOAD_FILE', - 'LOWER', - 'LTRIM', - 'MD5', - 'MONTHNAME', - 'OLD_PASSWORD', - 'PASSWORD', - 'QUOTE', - 'REVERSE', - 'RTRIM', - 'SHA1', - 'SOUNDEX', - 'SPACE', - 'TRIM', - 'UNCOMPRESS', - 'UNHEX', - 'UPPER', - 'USER', - 'UUID', - 'VERSION', + '0' => 'AES_DECRYPT', + '1' => 'AES_ENCRYPT', + '2' => 'BIN', + '3' => 'CHAR', + '4' => 'COMPRESS', + '5' => 'CURRENT_USER', + '6' => 'DATABASE', + '7' => 'DAYNAME', + '8' => 'DES_DECRYPT', + '9' => 'DES_ENCRYPT', + '10' => 'ENCRYPT', + '11' => 'HEX', + '12' => 'INET6_NTOA', + '13' => 'INET_NTOA', + '14' => 'LOAD_FILE', + '15' => 'LOWER', + '16' => 'LTRIM', + '17' => 'MD5', + '18' => 'MONTHNAME', + '19' => 'OLD_PASSWORD', + '20' => 'PASSWORD', + '21' => 'QUOTE', + '22' => 'REVERSE', + '23' => 'RTRIM', + '24' => 'SHA1', + '25' => 'SOUNDEX', + '26' => 'SPACE', + '27' => 'TRIM', + '28' => 'UNCOMPRESS', + '29' => 'UNHEX', + '30' => 'UPPER', + '31' => 'USER', + '33' => 'VERSION', ], $this->object->getFunctions('enum') ); @@ -640,8 +637,6 @@ class TypesTest extends AbstractTestCase 'UTC_DATE', 'UTC_TIME', 'UTC_TIMESTAMP', - 'UUID', - 'UUID_SHORT', 'VERSION', 'WEEK', 'WEEKDAY', From 033a4cb099d540a24b1e883ef5c647eb49fa96e6 Mon Sep 17 00:00:00 2001 From: Mo Sureerat Date: Mon, 10 Oct 2022 22:51:31 +0700 Subject: [PATCH 02/16] Update unit test - Exclude UUID if not supported database version Signed-off-by: Mo Sureerat --- test/classes/InsertEditTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/test/classes/InsertEditTest.php b/test/classes/InsertEditTest.php index 590fa810d3..57d018a92d 100644 --- a/test/classes/InsertEditTest.php +++ b/test/classes/InsertEditTest.php @@ -2820,7 +2820,6 @@ class InsertEditTest extends AbstractTestCase ] ); $this->assertStringContainsString('qwerty', $actual); - $this->assertStringContainsString('', $actual); $this->assertStringContainsString('datetime', $actual); $this->assertStringContainsString( ' Date: Tue, 11 Oct 2022 01:17:29 +0700 Subject: [PATCH 03/16] Relocate method and fix unit test fail Signed-off-by: Mo Sureerat --- libraries/classes/DatabaseInterface.php | 15 +++++++++++++++ libraries/classes/Query/Compatibility.php | 15 +++++++++++++++ libraries/classes/Types.php | 23 ++++------------------- test/classes/InsertEditTest.php | 6 ++++++ 4 files changed, 40 insertions(+), 19 deletions(-) diff --git a/libraries/classes/DatabaseInterface.php b/libraries/classes/DatabaseInterface.php index eb7e56501b..f966ecbdf1 100644 --- a/libraries/classes/DatabaseInterface.php +++ b/libraries/classes/DatabaseInterface.php @@ -2282,6 +2282,21 @@ class DatabaseInterface implements DbalInterface return $this->isPercona; } + /** + * Set server version + */ + public function setDatabaseVersion(string $database, string $version): void + { + $this->versionString = $version; + $this->versionInt = Utilities::versionToInt($version); + + if ($database !== 'mariadb') { + return; + } + + $this->isMariaDb = true; + } + /** * Load correct database driver * diff --git a/libraries/classes/Query/Compatibility.php b/libraries/classes/Query/Compatibility.php index 5f995951f6..04d68ff1bb 100644 --- a/libraries/classes/Query/Compatibility.php +++ b/libraries/classes/Query/Compatibility.php @@ -19,6 +19,8 @@ use function substr; */ class Compatibility { + private const UUID_SUPPORT_MARIADB_VERSION = 100700; // 10.7.0 + public static function getISCompatForGetTablesFull(array $eachTables, string $eachDatabase): array { foreach ($eachTables as $table_name => $_) { @@ -200,6 +202,19 @@ class Compatibility return false; } + /** + * Check whether the database support uuid + * true if uuid is supported + */ + public static function isUUIDSupported(DatabaseInterface $dbi): bool + { + if (! $dbi->isMariaDB()) { + return false; + } + + return $dbi->getVersion() >= self::UUID_SUPPORT_MARIADB_VERSION; + } + /** * Returns whether the database server supports virtual columns */ diff --git a/libraries/classes/Types.php b/libraries/classes/Types.php index 47c7230e14..9a8c459dbb 100644 --- a/libraries/classes/Types.php +++ b/libraries/classes/Types.php @@ -553,7 +553,7 @@ class Types $ret = array_diff($ret, ['INET6_NTOA']); } - if (! $this->isUUIDEnabled()) { + if (! Compatibility::isUUIDSupported($this->dbi)) { $ret = array_diff($ret, ['UUID']); } @@ -638,7 +638,7 @@ class Types $ret = array_diff($ret, ['INET6_ATON']); } - if (! $this->isUUIDEnabled()) { + if (! Compatibility::isUUIDSupported($this->dbi)) { $ret = array_diff($ret, ['UUID_SHORT']); } @@ -770,7 +770,7 @@ class Types 'DATE', ]; - if ($this->isUUIDEnabled()) { + if (Compatibility::isUUIDSupported($this->dbi)) { $ret[] = 'UUID'; } @@ -844,7 +844,7 @@ class Types $ret['JSON'] = ['JSON']; } - if ($this->isUUIDEnabled()) { + if (Compatibility::isUUIDSupported($this->dbi)) { $ret['UUID'] = ['UUID']; } @@ -932,19 +932,4 @@ class Types '', ]; } - - /** - * Check whether the database support uuid - * true if uuid is supported - * - * @return bool enabled status - */ - private function isUUIDEnabled(): bool - { - if (! $this->dbi->isMariaDB()) { - return false; - } - - return $this->dbi->getVersion() >= self::UUID_SUPPORT_MARIADB_VERSION; - } } diff --git a/test/classes/InsertEditTest.php b/test/classes/InsertEditTest.php index 57d018a92d..e52ec03833 100644 --- a/test/classes/InsertEditTest.php +++ b/test/classes/InsertEditTest.php @@ -72,6 +72,11 @@ class InsertEditTest extends AbstractTestCase $GLOBALS['cfg']['Confirm'] = true; $GLOBALS['cfg']['LoginCookieValidity'] = 1440; $GLOBALS['cfg']['enable_drag_drop_import'] = true; + + if (! empty($GLOBALS['dbi'])) { + $GLOBALS['dbi']->setDatabaseVersion('mariadb', '10.7.0'); + } + $this->insertEdit = new InsertEdit($GLOBALS['dbi']); } @@ -2820,6 +2825,7 @@ class InsertEditTest extends AbstractTestCase ] ); $this->assertStringContainsString('qwerty', $actual); + $this->assertStringContainsString('', $actual); $this->assertStringContainsString('datetime', $actual); $this->assertStringContainsString( ' Date: Tue, 11 Oct 2022 01:21:32 +0700 Subject: [PATCH 04/16] Remove unused const Signed-off-by: Mo Sureerat --- libraries/classes/Types.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/classes/Types.php b/libraries/classes/Types.php index 9a8c459dbb..6ccdb32aac 100644 --- a/libraries/classes/Types.php +++ b/libraries/classes/Types.php @@ -25,8 +25,6 @@ use function strncasecmp; */ class Types { - private const UUID_SUPPORT_MARIADB_VERSION = 100700; // 10.7.0 - /** @var DatabaseInterface Database interface */ private $dbi; From 7ea0a9564694ecf04ed915147181327b2761fa4a Mon Sep 17 00:00:00 2001 From: Mo Sureerat Date: Tue, 11 Oct 2022 01:24:33 +0700 Subject: [PATCH 05/16] Update code style Signed-off-by: Mo Sureerat --- libraries/classes/DatabaseInterface.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/libraries/classes/DatabaseInterface.php b/libraries/classes/DatabaseInterface.php index f966ecbdf1..e090bf6b49 100644 --- a/libraries/classes/DatabaseInterface.php +++ b/libraries/classes/DatabaseInterface.php @@ -2283,18 +2283,14 @@ class DatabaseInterface implements DbalInterface } /** - * Set server version + * Set server version (initially to use in unit test) */ public function setDatabaseVersion(string $database, string $version): void { $this->versionString = $version; $this->versionInt = Utilities::versionToInt($version); - if ($database !== 'mariadb') { - return; - } - - $this->isMariaDb = true; + $this->isMariaDb = $database === 'mariadb'; } /** From 8a68bd639eda3c5382743cd0489137a1f249dd2a Mon Sep 17 00:00:00 2001 From: Mo Sureerat Date: Tue, 11 Oct 2022 01:56:44 +0700 Subject: [PATCH 06/16] Add unit test Signed-off-by: Mo Sureerat --- test/classes/Query/CompatibilityTest.php | 28 ++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/classes/Query/CompatibilityTest.php b/test/classes/Query/CompatibilityTest.php index c05c67f6f1..51341c38a5 100644 --- a/test/classes/Query/CompatibilityTest.php +++ b/test/classes/Query/CompatibilityTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace PhpMyAdmin\Tests\Query; +use PhpMyAdmin\DatabaseInterface; use PhpMyAdmin\Query\Compatibility; use PHPUnit\Framework\TestCase; @@ -35,4 +36,31 @@ class CompatibilityTest extends TestCase 'MariaDB 10.4.3' => [true, true, 100403], ]; } + + /** + * @dataProvider providerForTestIsUUIDSupported + */ + public function testIsUUIDSupported(bool $expected, bool $isMariaDb, int $version): void + { + $dbiStub = $this->createStub(DatabaseInterface::class); + + $dbiStub->method('isMariaDB')->willReturn($isMariaDb); + $dbiStub->method('getVersion')->willReturn($version); + + $this->assertSame($expected, Compatibility::isUUIDSupported($dbiStub)); + } + + /** + * @return array[] + * @psalm-return array + */ + public function providerForTestIsUUIDSupported(): array + { + return [ + 'MySQL 5.7.5' => [false, false, 50705], + 'MySQL 8.0.30' => [false, false, 80030], + 'MariaDB 10.6.0' => [false, true, 100600], + 'MariaDB 10.7.0' => [true, true, 100700], + ]; + } } From b7bb08ddaa461feb0ff4ecf2bfa0a7fc6336f977 Mon Sep 17 00:00:00 2001 From: Mo Sureerat Date: Tue, 11 Oct 2022 02:09:56 +0700 Subject: [PATCH 07/16] Improve code Signed-off-by: Mo Sureerat --- libraries/classes/Types.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libraries/classes/Types.php b/libraries/classes/Types.php index 6ccdb32aac..7e28a2d391 100644 --- a/libraries/classes/Types.php +++ b/libraries/classes/Types.php @@ -507,6 +507,7 @@ class Types { $isMariaDB = $this->dbi->isMariaDB(); $serverVersion = $this->dbi->getVersion(); + $isUUIDSupported = Compatibility::isUUIDSupported($this->dbi); switch ($class) { case 'CHAR': @@ -551,7 +552,7 @@ class Types $ret = array_diff($ret, ['INET6_NTOA']); } - if (! Compatibility::isUUIDSupported($this->dbi)) { + if (! $isUUIDSupported) { $ret = array_diff($ret, ['UUID']); } @@ -636,7 +637,7 @@ class Types $ret = array_diff($ret, ['INET6_ATON']); } - if (! Compatibility::isUUIDSupported($this->dbi)) { + if (! $isUUIDSupported) { $ret = array_diff($ret, ['UUID_SHORT']); } @@ -759,6 +760,7 @@ class Types { $isMariaDB = $this->dbi->isMariaDB(); $serverVersion = $this->dbi->getVersion(); + $isUUIDSupported = Compatibility::isUUIDSupported($this->dbi); // most used types $ret = [ @@ -768,7 +770,7 @@ class Types 'DATE', ]; - if (Compatibility::isUUIDSupported($this->dbi)) { + if ($isUUIDSupported) { $ret[] = 'UUID'; } @@ -842,7 +844,7 @@ class Types $ret['JSON'] = ['JSON']; } - if (Compatibility::isUUIDSupported($this->dbi)) { + if ($isUUIDSupported) { $ret['UUID'] = ['UUID']; } From bd8a9efce0962cc86435fe5ef0478a0f2a212bca Mon Sep 17 00:00:00 2001 From: Mo Sureerat Date: Tue, 11 Oct 2022 02:43:16 +0700 Subject: [PATCH 08/16] Add more test for coverage Signed-off-by: Mo Sureerat --- test/classes/DatabaseInterfaceTest.php | 38 ++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/test/classes/DatabaseInterfaceTest.php b/test/classes/DatabaseInterfaceTest.php index 84c28aa5d1..a54c44f489 100644 --- a/test/classes/DatabaseInterfaceTest.php +++ b/test/classes/DatabaseInterfaceTest.php @@ -702,4 +702,42 @@ class DatabaseInterfaceTest extends AbstractTestCase $this->assertAllQueriesConsumed(); } + + /** + * Tests for setDatabaseVersion method. + * + * @param string $database Database name + * @param string $version Database version + * @param int $versionInt Database version as integer + * @param bool $isMariaDb True if mariadb + * + * @dataProvider databaseVersionData + */ + public function testSetDatabaseVersion( + string $database, + string $version, + int $versionInt, + bool $isMariaDb + ): void { + $this->dbi->setDatabaseVersion($database, $version); + + $this->assertEquals($versionInt, $this->dbi->getVersion()); + $this->assertEquals($isMariaDb, $this->dbi->isMariaDb()); + $this->assertEquals($version, $this->dbi->getVersionString()); + } + + /** + * Data provider for setDatabaseVersion() tests. + * + * @return array + * @psalm-return array + */ + public function databaseVersionData(): array + { + return [ + ['percona', '6.1.0', 60100, false], + ['mysql', '7.10.3', 71003, false], + ['mariadb', '10.01.40', 100140, true], + ]; + } } From cac09436c948e80be25c6da90636349ccc4df31b Mon Sep 17 00:00:00 2001 From: Mo Sureerat Date: Tue, 11 Oct 2022 13:00:42 +0700 Subject: [PATCH 09/16] Rewrite method to set database info Signed-off-by: Mo Sureerat --- libraries/classes/DatabaseInterface.php | 30 +++++++-------- test/classes/DatabaseInterfaceTest.php | 50 +++++++++++++++++-------- test/classes/InsertEditTest.php | 5 ++- 3 files changed, 54 insertions(+), 31 deletions(-) diff --git a/libraries/classes/DatabaseInterface.php b/libraries/classes/DatabaseInterface.php index e090bf6b49..7f5b8d0b0d 100644 --- a/libraries/classes/DatabaseInterface.php +++ b/libraries/classes/DatabaseInterface.php @@ -1087,16 +1087,7 @@ class DatabaseInterface implements DbalInterface $version = $this->fetchSingleRow('SELECT @@version, @@version_comment'); if (is_array($version)) { - $this->versionString = $version['@@version'] ?? ''; - $this->versionInt = Utilities::versionToInt($this->versionString); - $this->versionComment = $version['@@version_comment'] ?? ''; - if (stripos($this->versionString, 'mariadb') !== false) { - $this->isMariaDb = true; - } - - if (stripos($this->versionComment, 'percona') !== false) { - $this->isPercona = true; - } + $this->setVersion($version); } if ($this->versionInt > 50503) { @@ -2283,14 +2274,23 @@ class DatabaseInterface implements DbalInterface } /** - * Set server version (initially to use in unit test) + * Set version */ - public function setDatabaseVersion(string $database, string $version): void + public function setVersion(array $version): void { - $this->versionString = $version; - $this->versionInt = Utilities::versionToInt($version); + $this->versionString = $version['@@version'] ?? ''; + $this->versionInt = Utilities::versionToInt($this->versionString); + $this->versionComment = $version['@@version_comment'] ?? ''; - $this->isMariaDb = $database === 'mariadb'; + if (stripos($this->versionString, 'mariadb') !== false) { + $this->isMariaDb = true; + } + + if (stripos($this->versionComment, 'percona') === false) { + return; + } + + $this->isPercona = true; } /** diff --git a/test/classes/DatabaseInterfaceTest.php b/test/classes/DatabaseInterfaceTest.php index a54c44f489..8a505cba86 100644 --- a/test/classes/DatabaseInterfaceTest.php +++ b/test/classes/DatabaseInterfaceTest.php @@ -704,40 +704,60 @@ class DatabaseInterfaceTest extends AbstractTestCase } /** - * Tests for setDatabaseVersion method. + * Tests for setVersion method. * - * @param string $database Database name - * @param string $version Database version - * @param int $versionInt Database version as integer - * @param bool $isMariaDb True if mariadb + * @param array $version Database version + * @param int $versionInt Database version as integer + * @param bool $isMariaDb True if mariadb + * @phpstan-param array $version * * @dataProvider databaseVersionData */ - public function testSetDatabaseVersion( - string $database, - string $version, + public function testSetVersion( + array $version, int $versionInt, bool $isMariaDb ): void { - $this->dbi->setDatabaseVersion($database, $version); + $this->dbi->setVersion($version); $this->assertEquals($versionInt, $this->dbi->getVersion()); $this->assertEquals($isMariaDb, $this->dbi->isMariaDb()); - $this->assertEquals($version, $this->dbi->getVersionString()); + $this->assertEquals($version['@@version'], $this->dbi->getVersionString()); } /** - * Data provider for setDatabaseVersion() tests. + * Data provider for setVersion() tests. * * @return array - * @psalm-return array + * @psalm-return array, int, bool}> */ public function databaseVersionData(): array { return [ - ['percona', '6.1.0', 60100, false], - ['mysql', '7.10.3', 71003, false], - ['mariadb', '10.01.40', 100140, true], + [ + [ + '@@version' => '6.1.0', + '@@version_comment' => 'Percona Server for Test', + ], + 60100, + false, + ], + [ + [ + '@@version' => '7.10.3', + '@@version_comment' => 'MySQL Community Server (GPL)', + ], + 71003, + false, + ], + [ + [ + '@@version' => '10.01.40-MariaDB-1:10.01.40+maria~ubu2204', + '@@version_comment' => 'mariadb.org binary distribution', + ], + 100140, + true, + ], ]; } } diff --git a/test/classes/InsertEditTest.php b/test/classes/InsertEditTest.php index e52ec03833..549cac76df 100644 --- a/test/classes/InsertEditTest.php +++ b/test/classes/InsertEditTest.php @@ -74,7 +74,10 @@ class InsertEditTest extends AbstractTestCase $GLOBALS['cfg']['enable_drag_drop_import'] = true; if (! empty($GLOBALS['dbi'])) { - $GLOBALS['dbi']->setDatabaseVersion('mariadb', '10.7.0'); + $GLOBALS['dbi']->setVersion([ + '@@version' => '10.9.3-MariaDB-1:10.9.3+maria~ubu2204', + '@@version_comment' => 'mariadb.org binary distribution', + ]); } $this->insertEdit = new InsertEdit($GLOBALS['dbi']); From d97df6c00b8dcc63e2cd22fff01073c46dae20cb Mon Sep 17 00:00:00 2001 From: Mo Sureerat Date: Tue, 11 Oct 2022 13:12:07 +0700 Subject: [PATCH 10/16] Define type Signed-off-by: Mo Sureerat --- libraries/classes/DatabaseInterface.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/classes/DatabaseInterface.php b/libraries/classes/DatabaseInterface.php index 7f5b8d0b0d..996e434c9d 100644 --- a/libraries/classes/DatabaseInterface.php +++ b/libraries/classes/DatabaseInterface.php @@ -2275,6 +2275,9 @@ class DatabaseInterface implements DbalInterface /** * Set version + * + * @param array $version Database version information + * @phpstan-param array $version */ public function setVersion(array $version): void { From 81bdecf713e849347ac23aafc714485aca186e06 Mon Sep 17 00:00:00 2001 From: Mo Sureerat Date: Tue, 11 Oct 2022 13:21:35 +0700 Subject: [PATCH 11/16] Improve test Signed-off-by: Mo Sureerat --- test/classes/DatabaseInterfaceTest.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/classes/DatabaseInterfaceTest.php b/test/classes/DatabaseInterfaceTest.php index 8a505cba86..74083fb510 100644 --- a/test/classes/DatabaseInterfaceTest.php +++ b/test/classes/DatabaseInterfaceTest.php @@ -709,6 +709,7 @@ class DatabaseInterfaceTest extends AbstractTestCase * @param array $version Database version * @param int $versionInt Database version as integer * @param bool $isMariaDb True if mariadb + * @param bool $isPercona True if percona * @phpstan-param array $version * * @dataProvider databaseVersionData @@ -716,12 +717,14 @@ class DatabaseInterfaceTest extends AbstractTestCase public function testSetVersion( array $version, int $versionInt, - bool $isMariaDb + bool $isMariaDb, + bool $isPercona ): void { $this->dbi->setVersion($version); $this->assertEquals($versionInt, $this->dbi->getVersion()); $this->assertEquals($isMariaDb, $this->dbi->isMariaDb()); + $this->assertEquals($isPercona, $this->dbi->isPercona()); $this->assertEquals($version['@@version'], $this->dbi->getVersionString()); } @@ -729,7 +732,7 @@ class DatabaseInterfaceTest extends AbstractTestCase * Data provider for setVersion() tests. * * @return array - * @psalm-return array, int, bool}> + * @psalm-return array, int, bool, bool}> */ public function databaseVersionData(): array { @@ -737,10 +740,11 @@ class DatabaseInterfaceTest extends AbstractTestCase [ [ '@@version' => '6.1.0', - '@@version_comment' => 'Percona Server for Test', + '@@version_comment' => "Percona Server (GPL), Release '11', Revision 'c1y2gr1df4a'", ], 60100, false, + true, ], [ [ @@ -749,6 +753,7 @@ class DatabaseInterfaceTest extends AbstractTestCase ], 71003, false, + false, ], [ [ @@ -757,6 +762,7 @@ class DatabaseInterfaceTest extends AbstractTestCase ], 100140, true, + false, ], ]; } From 9ff395e8881cfb30492a5552ce583669a5db5686 Mon Sep 17 00:00:00 2001 From: Mo Sureerat Date: Tue, 11 Oct 2022 13:40:20 +0700 Subject: [PATCH 12/16] Fix type Signed-off-by: Mo Sureerat --- libraries/classes/DatabaseInterface.php | 2 +- test/classes/DatabaseInterfaceTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/classes/DatabaseInterface.php b/libraries/classes/DatabaseInterface.php index 996e434c9d..c0033b5eb4 100644 --- a/libraries/classes/DatabaseInterface.php +++ b/libraries/classes/DatabaseInterface.php @@ -2277,7 +2277,7 @@ class DatabaseInterface implements DbalInterface * Set version * * @param array $version Database version information - * @phpstan-param array $version + * @phpstan-param array $version */ public function setVersion(array $version): void { diff --git a/test/classes/DatabaseInterfaceTest.php b/test/classes/DatabaseInterfaceTest.php index 74083fb510..0759077575 100644 --- a/test/classes/DatabaseInterfaceTest.php +++ b/test/classes/DatabaseInterfaceTest.php @@ -710,7 +710,7 @@ class DatabaseInterfaceTest extends AbstractTestCase * @param int $versionInt Database version as integer * @param bool $isMariaDb True if mariadb * @param bool $isPercona True if percona - * @phpstan-param array $version + * @phpstan-param array $version * * @dataProvider databaseVersionData */ @@ -732,7 +732,7 @@ class DatabaseInterfaceTest extends AbstractTestCase * Data provider for setVersion() tests. * * @return array - * @psalm-return array, int, bool, bool}> + * @psalm-return array, int, bool, bool}> */ public function databaseVersionData(): array { From a07474f9970d6e35124164abcd34606e4507891f Mon Sep 17 00:00:00 2001 From: Mo Sureerat Date: Tue, 11 Oct 2022 17:32:03 +0700 Subject: [PATCH 13/16] Handle array re-index + fix setVersion logic + hardcode version int Signed-off-by: Mo Sureerat --- libraries/classes/DatabaseInterface.php | 11 +- libraries/classes/Query/Compatibility.php | 4 +- libraries/classes/Types.php | 5 +- test/classes/TypesTest.php | 234 +++++++++++----------- 4 files changed, 123 insertions(+), 131 deletions(-) diff --git a/libraries/classes/DatabaseInterface.php b/libraries/classes/DatabaseInterface.php index c0033b5eb4..7d5f7a0a8b 100644 --- a/libraries/classes/DatabaseInterface.php +++ b/libraries/classes/DatabaseInterface.php @@ -2285,15 +2285,8 @@ class DatabaseInterface implements DbalInterface $this->versionInt = Utilities::versionToInt($this->versionString); $this->versionComment = $version['@@version_comment'] ?? ''; - if (stripos($this->versionString, 'mariadb') !== false) { - $this->isMariaDb = true; - } - - if (stripos($this->versionComment, 'percona') === false) { - return; - } - - $this->isPercona = true; + $this->isMariaDb = stripos($this->versionString, 'mariadb') !== false; + $this->isPercona = stripos($this->versionComment, 'percona') !== false; } /** diff --git a/libraries/classes/Query/Compatibility.php b/libraries/classes/Query/Compatibility.php index 04d68ff1bb..5e8d58df44 100644 --- a/libraries/classes/Query/Compatibility.php +++ b/libraries/classes/Query/Compatibility.php @@ -19,8 +19,6 @@ use function substr; */ class Compatibility { - private const UUID_SUPPORT_MARIADB_VERSION = 100700; // 10.7.0 - public static function getISCompatForGetTablesFull(array $eachTables, string $eachDatabase): array { foreach ($eachTables as $table_name => $_) { @@ -212,7 +210,7 @@ class Compatibility return false; } - return $dbi->getVersion() >= self::UUID_SUPPORT_MARIADB_VERSION; + return $dbi->getVersion() >= 100700; // 10.7.0 } /** diff --git a/libraries/classes/Types.php b/libraries/classes/Types.php index 7e28a2d391..5c65b4e44b 100644 --- a/libraries/classes/Types.php +++ b/libraries/classes/Types.php @@ -13,6 +13,7 @@ use function __; use function _pgettext; use function array_diff; use function array_merge; +use function array_values; use function htmlspecialchars; use function in_array; use function mb_strtoupper; @@ -556,7 +557,7 @@ class Types $ret = array_diff($ret, ['UUID']); } - return $ret; + return array_values($ret); case 'DATE': return [ @@ -641,7 +642,7 @@ class Types $ret = array_diff($ret, ['UUID_SHORT']); } - return $ret; + return array_values($ret); case 'SPATIAL': if ($serverVersion >= 50600) { diff --git a/test/classes/TypesTest.php b/test/classes/TypesTest.php index 479ef61236..ce15fdb88b 100644 --- a/test/classes/TypesTest.php +++ b/test/classes/TypesTest.php @@ -335,39 +335,39 @@ class TypesTest extends AbstractTestCase [ 'CHAR', [ - '0' => 'AES_DECRYPT', - '1' => 'AES_ENCRYPT', - '2' => 'BIN', - '3' => 'CHAR', - '4' => 'COMPRESS', - '5' => 'CURRENT_USER', - '6' => 'DATABASE', - '7' => 'DAYNAME', - '8' => 'DES_DECRYPT', - '9' => 'DES_ENCRYPT', - '10' => 'ENCRYPT', - '11' => 'HEX', - '12' => 'INET6_NTOA', - '13' => 'INET_NTOA', - '14' => 'LOAD_FILE', - '15' => 'LOWER', - '16' => 'LTRIM', - '17' => 'MD5', - '18' => 'MONTHNAME', - '19' => 'OLD_PASSWORD', - '20' => 'PASSWORD', - '21' => 'QUOTE', - '22' => 'REVERSE', - '23' => 'RTRIM', - '24' => 'SHA1', - '25' => 'SOUNDEX', - '26' => 'SPACE', - '27' => 'TRIM', - '28' => 'UNCOMPRESS', - '29' => 'UNHEX', - '30' => 'UPPER', - '31' => 'USER', - '33' => 'VERSION', + 'AES_DECRYPT', + 'AES_ENCRYPT', + 'BIN', + 'CHAR', + 'COMPRESS', + 'CURRENT_USER', + 'DATABASE', + 'DAYNAME', + 'DES_DECRYPT', + 'DES_ENCRYPT', + 'ENCRYPT', + 'HEX', + 'INET6_NTOA', + 'INET_NTOA', + 'LOAD_FILE', + 'LOWER', + 'LTRIM', + 'MD5', + 'MONTHNAME', + 'OLD_PASSWORD', + 'PASSWORD', + 'QUOTE', + 'REVERSE', + 'RTRIM', + 'SHA1', + 'SOUNDEX', + 'SPACE', + 'TRIM', + 'UNCOMPRESS', + 'UNHEX', + 'UPPER', + 'USER', + 'VERSION', ], ], [ @@ -416,57 +416,57 @@ class TypesTest extends AbstractTestCase [ 'NUMBER', [ - '0' => 'ABS', - '1' => 'ACOS', - '2' => 'ASCII', - '3' => 'ASIN', - '4' => 'ATAN', - '5' => 'BIT_LENGTH', - '6' => 'BIT_COUNT', - '7' => 'CEILING', - '8' => 'CHAR_LENGTH', - '9' => 'CONNECTION_ID', - '10' => 'COS', - '11' => 'COT', - '12' => 'CRC32', - '13' => 'DAYOFMONTH', - '14' => 'DAYOFWEEK', - '15' => 'DAYOFYEAR', - '16' => 'DEGREES', - '17' => 'EXP', - '18' => 'FLOOR', - '19' => 'HOUR', - '20' => 'INET6_ATON', - '21' => 'INET_ATON', - '22' => 'LENGTH', - '23' => 'LN', - '24' => 'LOG', - '25' => 'LOG2', - '26' => 'LOG10', - '27' => 'MICROSECOND', - '28' => 'MINUTE', - '29' => 'MONTH', - '30' => 'OCT', - '31' => 'ORD', - '32' => 'PI', - '33' => 'QUARTER', - '34' => 'RADIANS', - '35' => 'RAND', - '36' => 'ROUND', - '37' => 'SECOND', - '38' => 'SIGN', - '39' => 'SIN', - '40' => 'SQRT', - '41' => 'TAN', - '42' => 'TO_DAYS', - '43' => 'TO_SECONDS', - '44' => 'TIME_TO_SEC', - '45' => 'UNCOMPRESSED_LENGTH', - '46' => 'UNIX_TIMESTAMP', - '48' => 'WEEK', - '49' => 'WEEKDAY', - '50' => 'WEEKOFYEAR', - '51' => 'YEARWEEK', + 'ABS', + 'ACOS', + 'ASCII', + 'ASIN', + 'ATAN', + 'BIT_LENGTH', + 'BIT_COUNT', + 'CEILING', + 'CHAR_LENGTH', + 'CONNECTION_ID', + 'COS', + 'COT', + 'CRC32', + 'DAYOFMONTH', + 'DAYOFWEEK', + 'DAYOFYEAR', + 'DEGREES', + 'EXP', + 'FLOOR', + 'HOUR', + 'INET6_ATON', + 'INET_ATON', + 'LENGTH', + 'LN', + 'LOG', + 'LOG2', + 'LOG10', + 'MICROSECOND', + 'MINUTE', + 'MONTH', + 'OCT', + 'ORD', + 'PI', + 'QUARTER', + 'RADIANS', + 'RAND', + 'ROUND', + 'SECOND', + 'SIGN', + 'SIN', + 'SQRT', + 'TAN', + 'TO_DAYS', + 'TO_SECONDS', + 'TIME_TO_SEC', + 'UNCOMPRESSED_LENGTH', + 'UNIX_TIMESTAMP', + 'WEEK', + 'WEEKDAY', + 'WEEKOFYEAR', + 'YEARWEEK', ], ], [ @@ -483,39 +483,39 @@ class TypesTest extends AbstractTestCase { $this->assertEquals( [ - '0' => 'AES_DECRYPT', - '1' => 'AES_ENCRYPT', - '2' => 'BIN', - '3' => 'CHAR', - '4' => 'COMPRESS', - '5' => 'CURRENT_USER', - '6' => 'DATABASE', - '7' => 'DAYNAME', - '8' => 'DES_DECRYPT', - '9' => 'DES_ENCRYPT', - '10' => 'ENCRYPT', - '11' => 'HEX', - '12' => 'INET6_NTOA', - '13' => 'INET_NTOA', - '14' => 'LOAD_FILE', - '15' => 'LOWER', - '16' => 'LTRIM', - '17' => 'MD5', - '18' => 'MONTHNAME', - '19' => 'OLD_PASSWORD', - '20' => 'PASSWORD', - '21' => 'QUOTE', - '22' => 'REVERSE', - '23' => 'RTRIM', - '24' => 'SHA1', - '25' => 'SOUNDEX', - '26' => 'SPACE', - '27' => 'TRIM', - '28' => 'UNCOMPRESS', - '29' => 'UNHEX', - '30' => 'UPPER', - '31' => 'USER', - '33' => 'VERSION', + 'AES_DECRYPT', + 'AES_ENCRYPT', + 'BIN', + 'CHAR', + 'COMPRESS', + 'CURRENT_USER', + 'DATABASE', + 'DAYNAME', + 'DES_DECRYPT', + 'DES_ENCRYPT', + 'ENCRYPT', + 'HEX', + 'INET6_NTOA', + 'INET_NTOA', + 'LOAD_FILE', + 'LOWER', + 'LTRIM', + 'MD5', + 'MONTHNAME', + 'OLD_PASSWORD', + 'PASSWORD', + 'QUOTE', + 'REVERSE', + 'RTRIM', + 'SHA1', + 'SOUNDEX', + 'SPACE', + 'TRIM', + 'UNCOMPRESS', + 'UNHEX', + 'UPPER', + 'USER', + 'VERSION', ], $this->object->getFunctions('enum') ); From 7dc36075811f4ad68abcc34ca437cce16b8f48b0 Mon Sep 17 00:00:00 2001 From: Mo Sureerat Date: Tue, 11 Oct 2022 19:18:27 +0700 Subject: [PATCH 14/16] Improve code & comment Signed-off-by: Mo Sureerat --- libraries/classes/Query/Compatibility.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/libraries/classes/Query/Compatibility.php b/libraries/classes/Query/Compatibility.php index 5e8d58df44..975bba94c7 100644 --- a/libraries/classes/Query/Compatibility.php +++ b/libraries/classes/Query/Compatibility.php @@ -201,16 +201,12 @@ class Compatibility } /** - * Check whether the database support uuid + * Check whether the database supports UUID data type * true if uuid is supported */ public static function isUUIDSupported(DatabaseInterface $dbi): bool { - if (! $dbi->isMariaDB()) { - return false; - } - - return $dbi->getVersion() >= 100700; // 10.7.0 + return $dbi->isMariaDB() && $dbi->getVersion() >= 100700; // 10.7.0 } /** From 4d52baf70013d81aaa945f012efc81b72698abde Mon Sep 17 00:00:00 2001 From: Mo Sureerat Date: Wed, 12 Oct 2022 21:41:48 +0700 Subject: [PATCH 15/16] Add test case for Types & DatabaseInterface Signed-off-by: Mo Sureerat --- test/classes/DatabaseInterfaceTest.php | 21 +++++++++++++++------ test/classes/TypesTest.php | 11 +++++++++++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/test/classes/DatabaseInterfaceTest.php b/test/classes/DatabaseInterfaceTest.php index 0759077575..30f6045954 100644 --- a/test/classes/DatabaseInterfaceTest.php +++ b/test/classes/DatabaseInterfaceTest.php @@ -712,7 +712,7 @@ class DatabaseInterfaceTest extends AbstractTestCase * @param bool $isPercona True if percona * @phpstan-param array $version * - * @dataProvider databaseVersionData + * @dataProvider provideDatabaseVersionData */ public function testSetVersion( array $version, @@ -734,7 +734,7 @@ class DatabaseInterfaceTest extends AbstractTestCase * @return array * @psalm-return array, int, bool, bool}> */ - public function databaseVersionData(): array + public function provideDatabaseVersionData(): array { return [ [ @@ -746,6 +746,15 @@ class DatabaseInterfaceTest extends AbstractTestCase false, true, ], + [ + [ + '@@version' => '10.01.40-MariaDB-1:10.01.40+maria~ubu2204', + '@@version_comment' => 'mariadb.org binary distribution', + ], + 100140, + true, + false, + ], [ [ '@@version' => '7.10.3', @@ -757,11 +766,11 @@ class DatabaseInterfaceTest extends AbstractTestCase ], [ [ - '@@version' => '10.01.40-MariaDB-1:10.01.40+maria~ubu2204', - '@@version_comment' => 'mariadb.org binary distribution', + '@@version' => '5.5.0', + '@@version_comment' => '', ], - 100140, - true, + 50500, + false, false, ], ]; diff --git a/test/classes/TypesTest.php b/test/classes/TypesTest.php index ce15fdb88b..b736b65643 100644 --- a/test/classes/TypesTest.php +++ b/test/classes/TypesTest.php @@ -309,6 +309,9 @@ class TypesTest extends AbstractTestCase ['MULTILINESTRING'], ['MULTIPOLYGON'], ['GEOMETRYCOLLECTION'], + ['JSON'], + ['INET6'], + ['UUID'], ]; } @@ -773,6 +776,14 @@ class TypesTest extends AbstractTestCase 'SET', 'CHAR', ], + [ + 'JSON', + 'JSON', + ], + [ + 'UUID', + 'UUID', + ], [ 'UNKNOWN', '', From 97158c754b2facbc0d190913c8b70febe0388d9b Mon Sep 17 00:00:00 2001 From: Mo Sureerat Date: Wed, 12 Oct 2022 22:40:57 +0700 Subject: [PATCH 16/16] Add test case for DatabaseInterface Signed-off-by: Mo Sureerat --- test/classes/DatabaseInterfaceTest.php | 100 +++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/test/classes/DatabaseInterfaceTest.php b/test/classes/DatabaseInterfaceTest.php index 30f6045954..01e8fefc16 100644 --- a/test/classes/DatabaseInterfaceTest.php +++ b/test/classes/DatabaseInterfaceTest.php @@ -9,6 +9,7 @@ use PhpMyAdmin\Database\DatabaseList; use PhpMyAdmin\DatabaseInterface; use PhpMyAdmin\Dbal\ResultInterface; use PhpMyAdmin\Query\Utilities; +use PhpMyAdmin\SqlParser\Context; use PhpMyAdmin\SystemDatabase; use PhpMyAdmin\Utils\SessionCache; use stdClass; @@ -28,6 +29,18 @@ class DatabaseInterfaceTest extends AbstractTestCase $GLOBALS['server'] = 0; } + /** + * Tear down function for mockResponse method + */ + protected function tearDown(): void + { + parent::tearDown(); + + unset($GLOBALS['lang']); + unset($GLOBALS['cfg']['Server']['SessionTimeZone']); + Context::load(); + } + /** * Tests for DBI::getCurrentUser() method. * @@ -172,6 +185,93 @@ class DatabaseInterfaceTest extends AbstractTestCase $this->assertInstanceOf(DatabaseList::class, $GLOBALS['dblist']); } + /** + * Tests for DBI::postConnect() method. + * should not call setVersion method if cannot fetch version + */ + public function testPostConnectShouldNotCallSetVersionIfNoVersion(): void + { + $GLOBALS['lang'] = 'en'; + $GLOBALS['cfg']['Server']['SessionTimeZone'] = ''; + + $mock = $this->getMockBuilder(DatabaseInterface::class) + ->disableOriginalConstructor() + ->onlyMethods(['fetchSingleRow', 'query', 'setVersion']) + ->getMock(); + + $mock->expects($this->once()) + ->method('fetchSingleRow') + ->will($this->returnValue(null)); + + $mock->expects($this->never())->method('setVersion'); + + $mock->postConnect(); + } + + /** + * Tests for DBI::postConnect() method. + * should call setVersion method if $version has value + */ + public function testPostConnectShouldCallSetVersionOnce(): void + { + $GLOBALS['lang'] = 'en'; + $GLOBALS['cfg']['Server']['SessionTimeZone'] = ''; + $versionQueryResult = [ + '@@version' => '10.20.7-MariaDB-1:10.9.3+maria~ubu2204', + '@@version_comment' => 'mariadb.org binary distribution', + ]; + + $mock = $this->getMockBuilder(DatabaseInterface::class) + ->disableOriginalConstructor() + ->onlyMethods(['fetchSingleRow', 'query', 'setVersion']) + ->getMock(); + + $mock->expects($this->once()) + ->method('fetchSingleRow') + ->will($this->returnValue($versionQueryResult)); + + $mock->expects($this->once())->method('setVersion')->with($versionQueryResult); + + $mock->postConnect(); + } + + /** + * Tests for DBI::postConnect() method. + * should set version int, isMariaDB and isPercona + * + * @param array $version Database version + * @param int $versionInt Database version as integer + * @param bool $isMariaDb True if mariadb + * @param bool $isPercona True if percona + * @phpstan-param array $version + * + * @dataProvider provideDatabaseVersionData + */ + public function testPostConnectShouldSetVersion( + array $version, + int $versionInt, + bool $isMariaDb, + bool $isPercona + ): void { + $GLOBALS['lang'] = 'en'; + $GLOBALS['cfg']['Server']['SessionTimeZone'] = ''; + + $mock = $this->getMockBuilder(DatabaseInterface::class) + ->disableOriginalConstructor() + ->onlyMethods(['fetchSingleRow', 'query']) + ->getMock(); + + $mock->expects($this->once()) + ->method('fetchSingleRow') + ->will($this->returnValue($version)); + + $mock->postConnect(); + + $this->assertEquals($mock->getVersion(), $versionInt); + $this->assertEquals($mock->isMariaDB(), $isMariaDb); + $this->assertEquals($mock->isPercona(), $isPercona); + } + /** * Test for getDbCollation */