Merge #17767 - Fix #17248 - Support UUID data type for MariaDB

Pull-request: #17767
Fixes: #17248

Signed-off-by: William Desportes <williamdes@wdes.fr>
This commit is contained in:
William Desportes 2022-10-12 19:32:10 +02:00
commit 44a349dd93
No known key found for this signature in database
GPG Key ID: 90A0EF1B8251A889
8 changed files with 1361 additions and 68 deletions

View File

@ -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<array-key, mixed> $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
*

View File

@ -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
*/

View File

@ -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;
}

View File

@ -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<array-key, mixed> $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<array-key, mixed> $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, array{array<array-key, mixed>, 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,
],
];
}
}

View File

@ -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']);
}

View File

@ -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<string, array{bool, bool, int}>
*/
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],
];
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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',
'',