diff --git a/libraries/classes/DatabaseInterface.php b/libraries/classes/DatabaseInterface.php index eb7e56501b..7d5f7a0a8b 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) { @@ -2282,6 +2273,22 @@ class DatabaseInterface implements DbalInterface return $this->isPercona; } + /** + * Set version + * + * @param array $version Database version information + * @phpstan-param array $version + */ + public function setVersion(array $version): void + { + $this->versionString = $version['@@version'] ?? ''; + $this->versionInt = Utilities::versionToInt($this->versionString); + $this->versionComment = $version['@@version_comment'] ?? ''; + + $this->isMariaDb = stripos($this->versionString, 'mariadb') !== false; + $this->isPercona = stripos($this->versionComment, 'percona') !== false; + } + /** * Load correct database driver * diff --git a/libraries/classes/Query/Compatibility.php b/libraries/classes/Query/Compatibility.php index 5f995951f6..975bba94c7 100644 --- a/libraries/classes/Query/Compatibility.php +++ b/libraries/classes/Query/Compatibility.php @@ -200,6 +200,15 @@ class Compatibility return false; } + /** + * Check whether the database supports UUID data type + * true if uuid is supported + */ + public static function isUUIDSupported(DatabaseInterface $dbi): bool + { + return $dbi->isMariaDB() && $dbi->getVersion() >= 100700; // 10.7.0 + } + /** * Returns whether the database server supports virtual columns */ diff --git a/libraries/classes/Types.php b/libraries/classes/Types.php index 4c2b2eeb78..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; @@ -418,6 +419,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 +489,9 @@ class Types case 'JSON': return 'JSON'; + + case 'UUID': + return 'UUID'; } return ''; @@ -501,6 +508,7 @@ class Types { $isMariaDB = $this->dbi->isMariaDB(); $serverVersion = $this->dbi->getVersion(); + $isUUIDSupported = Compatibility::isUUIDSupported($this->dbi); switch ($class) { case 'CHAR': @@ -545,7 +553,11 @@ class Types $ret = array_diff($ret, ['INET6_NTOA']); } - return $ret; + if (! $isUUIDSupported) { + $ret = array_diff($ret, ['UUID']); + } + + return array_values($ret); case 'DATE': return [ @@ -621,11 +633,16 @@ class Types 'WEEKOFYEAR', 'YEARWEEK', ]; + if (($isMariaDB && $serverVersion < 100012) || $serverVersion < 50603) { $ret = array_diff($ret, ['INET6_ATON']); } - return $ret; + if (! $isUUIDSupported) { + $ret = array_diff($ret, ['UUID_SHORT']); + } + + return array_values($ret); case 'SPATIAL': if ($serverVersion >= 50600) { @@ -744,6 +761,7 @@ class Types { $isMariaDB = $this->dbi->isMariaDB(); $serverVersion = $this->dbi->getVersion(); + $isUUIDSupported = Compatibility::isUUIDSupported($this->dbi); // most used types $ret = [ @@ -752,6 +770,11 @@ class Types 'TEXT', 'DATE', ]; + + if ($isUUIDSupported) { + $ret[] = 'UUID'; + } + // numeric $ret[_pgettext('numeric types', 'Numeric')] = [ 'TINYINT', @@ -822,6 +845,10 @@ class Types $ret['JSON'] = ['JSON']; } + if ($isUUIDSupported) { + $ret['UUID'] = ['UUID']; + } + return $ret; } diff --git a/test/classes/DatabaseInterfaceTest.php b/test/classes/DatabaseInterfaceTest.php index 84c28aa5d1..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 */ @@ -702,4 +802,77 @@ class DatabaseInterfaceTest extends AbstractTestCase $this->assertAllQueriesConsumed(); } + + /** + * Tests for setVersion method. + * + * @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 testSetVersion( + array $version, + int $versionInt, + 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()); + } + + /** + * Data provider for setVersion() tests. + * + * @return array + * @psalm-return array, int, bool, bool}> + */ + public function provideDatabaseVersionData(): array + { + return [ + [ + [ + '@@version' => '6.1.0', + '@@version_comment' => "Percona Server (GPL), Release '11', Revision 'c1y2gr1df4a'", + ], + 60100, + 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', + '@@version_comment' => 'MySQL Community Server (GPL)', + ], + 71003, + false, + false, + ], + [ + [ + '@@version' => '5.5.0', + '@@version_comment' => '', + ], + 50500, + false, + false, + ], + ]; + } } diff --git a/test/classes/InsertEditTest.php b/test/classes/InsertEditTest.php index 590fa810d3..549cac76df 100644 --- a/test/classes/InsertEditTest.php +++ b/test/classes/InsertEditTest.php @@ -72,6 +72,14 @@ 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']->setVersion([ + '@@version' => '10.9.3-MariaDB-1:10.9.3+maria~ubu2204', + '@@version_comment' => 'mariadb.org binary distribution', + ]); + } + $this->insertEdit = new InsertEdit($GLOBALS['dbi']); } 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], + ]; + } } 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..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'], ]; } @@ -367,7 +370,6 @@ class TypesTest extends AbstractTestCase 'UNHEX', 'UPPER', 'USER', - 'UUID', 'VERSION', ], ], @@ -417,58 +419,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', - '47' => 'UUID_SHORT', - '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', ], ], [ @@ -517,7 +518,6 @@ class TypesTest extends AbstractTestCase 'UNHEX', 'UPPER', 'USER', - 'UUID', 'VERSION', ], $this->object->getFunctions('enum') @@ -640,8 +640,6 @@ class TypesTest extends AbstractTestCase 'UTC_DATE', 'UTC_TIME', 'UTC_TIMESTAMP', - 'UUID', - 'UUID_SHORT', 'VERSION', 'WEEK', 'WEEKDAY', @@ -778,6 +776,14 @@ class TypesTest extends AbstractTestCase 'SET', 'CHAR', ], + [ + 'JSON', + 'JSON', + ], + [ + 'UUID', + 'UUID', + ], [ 'UNKNOWN', '',