Unverified Commit 298c9140 authored by Luís Cobucci's avatar Luís Cobucci Committed by GitHub

Merge pull request #2825 from belgattitude/fix/mariadb/2824

MariaDB 10.2 initial support

Fixes: https://github.com/doctrine/dbal/issues/2819
parents edfbda15 628e8462
......@@ -21,7 +21,7 @@ before_install:
- mv ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini{,.disabled} || echo "xdebug not available"
before_script:
- if [[ "$DB" == "mysql" || "$DB" == "mysqli" || "$DB" == "mariadb" ]]; then mysql < tests/travis/create-mysql-schema.sql; fi;
- if [[ "$DB" == "mysql" || "$DB" == "mysqli" || "$DB" == *"mariadb"* ]]; then mysql < tests/travis/create-mysql-schema.sql; fi;
install:
- travis_retry composer -n install
......@@ -103,6 +103,38 @@ jobs:
addons:
mariadb: 10.1
- stage: Test
php: 7.1
env: DB=mariadb MARIADB_VERSION=10.2
addons:
mariadb: 10.2
- stage: Test
php: 7.2
env: DB=mariadb MARIADB_VERSION=10.2
addons:
mariadb: 10.2
- stage: Test
php: nightly
env: DB=mariadb MARIADB_VERSION=10.2
addons:
mariadb: 10.2
- stage: Test
php: 7.1
env: DB=mariadb.mysqli MARIADB_VERSION=10.2
addons:
mariadb: 10.2
- stage: Test
php: 7.2
env: DB=mariadb.mysqli MARIADB_VERSION=10.2
addons:
mariadb: 10.2
- stage: Test
php: nightly
env: DB=mariadb.mysqli MARIADB_VERSION=10.2
addons:
mariadb: 10.2
- stage: Test
php: 7.1
env: DB=pgsql POSTGRESQL_VERSION=9.2
......
......@@ -22,6 +22,7 @@ namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Platforms\MariaDb1027Platform;
use Doctrine\DBAL\Platforms\MySQL57Platform;
use Doctrine\DBAL\Platforms\MySqlPlatform;
use Doctrine\DBAL\Schema\MySqlSchemaManager;
......@@ -123,35 +124,74 @@ abstract class AbstractMySQLDriver implements Driver, ExceptionConverterDriver,
/**
* {@inheritdoc}
*
* @throws DBALException
*/
public function createDatabasePlatformForVersion($version)
{
if ( ! preg_match('/^(?P<major>\d+)(?:\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?)?/', $version, $versionParts)) {
throw DBALException::invalidPlatformVersionSpecified(
$version,
'<major_version>.<minor_version>.<patch_version>'
);
$mariadb = false !== stripos($version, 'mariadb');
if ($mariadb && version_compare($this->getMariaDbMysqlVersionNumber($version), '10.2.7', '>=')) {
return new MariaDb1027Platform();
}
if (false !== stripos($version, 'mariadb')) {
return $this->getDatabasePlatform();
if ( ! $mariadb && version_compare($this->getOracleMysqlVersionNumber($version), '5.7.9', '>=')) {
return new MySQL57Platform();
}
return $this->getDatabasePlatform();
}
/**
* Get a normalized 'version number' from the server string
* returned by Oracle MySQL servers.
*
* @param string $versionString Version string returned by the driver, i.e. '5.7.10'
* @throws DBALException
*/
private function getOracleMysqlVersionNumber(string $versionString) : string
{
if ( ! preg_match(
'/^(?P<major>\d+)(?:\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?)?/',
$versionString,
$versionParts
)) {
throw DBALException::invalidPlatformVersionSpecified(
$versionString,
'<major_version>.<minor_version>.<patch_version>'
);
}
$majorVersion = $versionParts['major'];
$minorVersion = isset($versionParts['minor']) ? $versionParts['minor'] : 0;
$patchVersion = isset($versionParts['patch']) ? $versionParts['patch'] : null;
$minorVersion = $versionParts['minor'] ?? 0;
$patchVersion = $versionParts['patch'] ?? null;
if ('5' === $majorVersion && '7' === $minorVersion && null === $patchVersion) {
$patchVersion = '9';
}
$version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion;
return $majorVersion . '.' . $minorVersion . '.' . $patchVersion;
}
if (version_compare($version, '5.7.9', '>=')) {
return new MySQL57Platform();
/**
* Detect MariaDB server version, including hack for some mariadb distributions
* that starts with the prefix '5.5.5-'
*
* @param string $versionString Version string as returned by mariadb server, i.e. '5.5.5-Mariadb-10.0.8-xenial'
* @throws DBALException
*/
private function getMariaDbMysqlVersionNumber(string $versionString) : string
{
if ( ! preg_match(
'/^(?:5\.5\.5-)?(mariadb-)?(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)/i',
$versionString,
$versionParts
)) {
throw DBALException::invalidPlatformVersionSpecified(
$versionString,
'^(?:5\.5\.5-)?(mariadb-)?<major_version>.<minor_version>.<patch_version>'
);
}
return $this->getDatabasePlatform();
return $versionParts['major'] . '.' . $versionParts['minor'] . '.' . $versionParts['patch'];
}
/**
......@@ -170,6 +210,7 @@ abstract class AbstractMySQLDriver implements Driver, ExceptionConverterDriver,
/**
* {@inheritdoc}
* @return MySqlPlatform
*/
public function getDatabasePlatform()
{
......@@ -178,6 +219,7 @@ abstract class AbstractMySQLDriver implements Driver, ExceptionConverterDriver,
/**
* {@inheritdoc}
* @return MySqlSchemaManager
*/
public function getSchemaManager(\Doctrine\DBAL\Connection $conn)
{
......
......@@ -94,9 +94,18 @@ class MysqliConnection implements Connection, PingableConnection, ServerInfoAwar
/**
* {@inheritdoc}
*
* The server version detection includes a special case for MariaDB
* to support '5.5.5-' prefixed versions introduced in Maria 10+
* @link https://jira.mariadb.org/browse/MDEV-4088
*/
public function getServerVersion()
{
$serverInfos = $this->_conn->get_server_info();
if (false !== stripos($serverInfos, 'mariadb')) {
return $serverInfos;
}
$majorVersion = floor($this->_conn->server_version / 10000);
$minorVersion = floor(($this->_conn->server_version - $majorVersion * 10000) / 100);
$patchVersion = floor($this->_conn->server_version - $majorVersion * 10000 - $minorVersion * 100);
......
......@@ -2287,19 +2287,19 @@ abstract class AbstractPlatform
$type = $field['type'];
if ($type instanceof Types\PhpIntegerMappingType) {
return " DEFAULT " . $default;
return ' DEFAULT ' . $default;
}
if ($type instanceof Types\PhpDateTimeMappingType && $default === $this->getCurrentTimestampSQL()) {
return " DEFAULT " . $this->getCurrentTimestampSQL();
return ' DEFAULT ' . $this->getCurrentTimestampSQL();
}
if ($type instanceof Types\TimeType && $default === $this->getCurrentTimeSQL()) {
return " DEFAULT " . $this->getCurrentTimeSQL();
return ' DEFAULT ' . $this->getCurrentTimeSQL();
}
if ($type instanceof Types\DateType && $default === $this->getCurrentDateSQL()) {
return " DEFAULT " . $this->getCurrentDateSQL();
return ' DEFAULT ' . $this->getCurrentDateSQL();
}
if ($type instanceof Types\BooleanType) {
......
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Platforms\Keywords;
/**
* MariaDb reserved keywords list.
* @link https://mariadb.com/kb/en/the-mariadb-library/reserved-words/
*/
final class MariaDb102Keywords extends MySQLKeywords
{
/**
* {@inheritdoc}
*/
public function getName() : string
{
return 'MariaDb102';
}
/**
* {@inheritdoc}
*/
protected function getKeywords() : array
{
return [
'ACCESSIBLE',
'ADD',
'ALL',
'ALTER',
'ANALYZE',
'AND',
'AS',
'ASC',
'ASENSITIVE',
'BEFORE',
'BETWEEN',
'BIGINT',
'BINARY',
'BLOB',
'BOTH',
'BY',
'CALL',
'CASCADE',
'CASE',
'CHANGE',
'CHAR',
'CHARACTER',
'CHECK',
'COLLATE',
'COLUMN',
'CONDITION',
'CONSTRAINT',
'CONTINUE',
'CONVERT',
'CREATE',
'CROSS',
'CURRENT_DATE',
'CURRENT_TIME',
'CURRENT_TIMESTAMP',
'CURRENT_USER',
'CURSOR',
'DATABASE',
'DATABASES',
'DAY_HOUR',
'DAY_MICROSECOND',
'DAY_MINUTE',
'DAY_SECOND',
'DEC',
'DECIMAL',
'DECLARE',
'DEFAULT',
'DELAYED',
'DELETE',
'DESC',
'DESCRIBE',
'DETERMINISTIC',
'DISTINCT',
'DISTINCTROW',
'DIV',
'DOUBLE',
'DROP',
'DUAL',
'EACH',
'ELSE',
'ELSEIF',
'ENCLOSED',
'ESCAPED',
'EXISTS',
'EXIT',
'EXPLAIN',
'FALSE',
'FETCH',
'FLOAT',
'FLOAT4',
'FLOAT8',
'FOR',
'FORCE',
'FOREIGN',
'FROM',
'FULLTEXT',
'GENERATED',
'GET',
'GENERAL',
'GRANT',
'GROUP',
'HAVING',
'HIGH_PRIORITY',
'HOUR_MICROSECOND',
'HOUR_MINUTE',
'HOUR_SECOND',
'IF',
'IGNORE',
'IGNORE_SERVER_IDS',
'IN',
'INDEX',
'INFILE',
'INNER',
'INOUT',
'INSENSITIVE',
'INSERT',
'INT',
'INT1',
'INT2',
'INT3',
'INT4',
'INT8',
'INTEGER',
'INTERVAL',
'INTO',
'IO_AFTER_GTIDS',
'IO_BEFORE_GTIDS',
'IS',
'ITERATE',
'JOIN',
'KEY',
'KEYS',
'KILL',
'LEADING',
'LEAVE',
'LEFT',
'LIKE',
'LIMIT',
'LINEAR',
'LINES',
'LOAD',
'LOCALTIME',
'LOCALTIMESTAMP',
'LOCK',
'LONG',
'LONGBLOB',
'LONGTEXT',
'LOOP',
'LOW_PRIORITY',
'MASTER_BIND',
'MASTER_HEARTBEAT_PERIOD',
'MASTER_SSL_VERIFY_SERVER_CERT',
'MATCH',
'MAXVALUE',
'MEDIUMBLOB',
'MEDIUMINT',
'MEDIUMTEXT',
'MIDDLEINT',
'MINUTE_MICROSECOND',
'MINUTE_SECOND',
'MOD',
'MODIFIES',
'NATURAL',
'NO_WRITE_TO_BINLOG',
'NOT',
'NULL',
'NUMERIC',
'ON',
'OPTIMIZE',
'OPTIMIZER_COSTS',
'OPTION',
'OPTIONALLY',
'OR',
'ORDER',
'OUT',
'OUTER',
'OUTFILE',
'PARTITION',
'PRECISION',
'PRIMARY',
'PROCEDURE',
'PURGE',
'RANGE',
'READ',
'READ_WRITE',
'READS',
'REAL',
'RECURSIVE',
'REFERENCES',
'REGEXP',
'RELEASE',
'RENAME',
'REPEAT',
'REPLACE',
'REQUIRE',
'RESIGNAL',
'RESTRICT',
'RETURN',
'REVOKE',
'RIGHT',
'RLIKE',
'ROWS',
'SCHEMA',
'SCHEMAS',
'SECOND_MICROSECOND',
'SELECT',
'SENSITIVE',
'SEPARATOR',
'SET',
'SHOW',
'SIGNAL',
'SLOW',
'SMALLINT',
'SPATIAL',
'SPECIFIC',
'SQL',
'SQL_BIG_RESULT',
'SQL_CALC_FOUND_ROWS',
'SQL_SMALL_RESULT',
'SQLEXCEPTION',
'SQLSTATE',
'SQLWARNING',
'SSL',
'STARTING',
'STORED',
'STRAIGHT_JOIN',
'TABLE',
'TERMINATED',
'THEN',
'TINYBLOB',
'TINYINT',
'TINYTEXT',
'TO',
'TRAILING',
'TRIGGER',
'TRUE',
'UNDO',
'UNION',
'UNIQUE',
'UNLOCK',
'UNSIGNED',
'UPDATE',
'USAGE',
'USE',
'USING',
'UTC_DATE',
'UTC_TIME',
'UTC_TIMESTAMP',
'VALUES',
'VARBINARY',
'VARCHAR',
'VARCHARACTER',
'VARYING',
'VIRTUAL',
'WHEN',
'WHERE',
'WHILE',
'WITH',
'WRITE',
'XOR',
'YEAR_MONTH',
'ZEROFILL',
];
}
}
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\DBAL\Platforms;
use Doctrine\DBAL\Types\Type;
/**
* Provides the behavior, features and SQL dialect of the MariaDB 10.2 (10.2.7 GA) database platform.
*
* Note: Should not be used with versions prior ro 10.2.7.
*
* @author Vanvelthem Sébastien
* @link www.doctrine-project.org
*/
final class MariaDb1027Platform extends MySqlPlatform
{
/**
* {@inheritdoc}
*/
public function hasNativeJsonType() : bool
{
return false;
}
/**
* {@inheritdoc}
*
* @link https://mariadb.com/kb/en/library/json-data-type/
*/
public function getJsonTypeDeclarationSQL(array $field) : string
{
return 'LONGTEXT';
}
/**
* {@inheritdoc}
*/
protected function getReservedKeywordsClass() : string
{
return Keywords\MariaDb102Keywords::class;
}
/**
* {@inheritdoc}
*/
protected function initializeDoctrineTypeMappings() : void
{
parent::initializeDoctrineTypeMappings();
$this->doctrineTypeMapping['json'] = Type::JSON;
}
}
......@@ -19,6 +19,7 @@
namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Platforms\MariaDb1027Platform;
use Doctrine\DBAL\Platforms\MySqlPlatform;
use Doctrine\DBAL\Types\Type;
......@@ -63,11 +64,11 @@ class MySqlSchemaManager extends AbstractSchemaManager
/**
* {@inheritdoc}
*/
protected function _getPortableTableIndexesList($tableIndexes, $tableName=null)
protected function _getPortableTableIndexesList($tableIndexes, $tableName = null)
{
foreach ($tableIndexes as $k => $v) {
$v = array_change_key_case($v, CASE_LOWER);
if ($v['key_name'] == 'PRIMARY') {
if ($v['key_name'] === 'PRIMARY') {
$v['primary'] = true;
} else {
$v['primary'] = false;
......@@ -120,14 +121,14 @@ class MySqlSchemaManager extends AbstractSchemaManager
$tableColumn['name'] = '';
}
$scale = null;
$scale = null;
$precision = null;
$type = $this->_platform->getDoctrineTypeMapping($dbType);
// In cases where not connected to a database DESCRIBE $table does not return 'Comment'
if (isset($tableColumn['comment'])) {
$type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type);
$type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type);
$tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type);
}
......@@ -143,8 +144,8 @@ class MySqlSchemaManager extends AbstractSchemaManager
case 'decimal':
if (preg_match('([A-Za-z]+\(([0-9]+)\,([0-9]+)\))', $tableColumn['type'], $match)) {
$precision = $match[1];
$scale = $match[2];
$length = null;
$scale = $match[2];
$length = null;
}
break;
case 'tinytext':
......@@ -176,25 +177,29 @@ class MySqlSchemaManager extends AbstractSchemaManager
break;
}
$length = ((int) $length == 0) ? null : (int) $length;
if ($this->_platform instanceof MariaDb1027Platform) {
$columnDefault = $this->getMariaDb1027ColumnDefault($this->_platform, $tableColumn['default']);
} else {
$columnDefault = $tableColumn['default'];
}
$options = [
'length' => $length,
'unsigned' => (bool) (strpos($tableColumn['type'], 'unsigned') !== false),
'length' => $length !== null ? (int) $length : null,
'unsigned' => strpos($tableColumn['type'], 'unsigned') !== false,
'fixed' => (bool) $fixed,
'default' => isset($tableColumn['default']) ? $tableColumn['default'] : null,
'notnull' => (bool) ($tableColumn['null'] != 'YES'),
'default' => $columnDefault,
'notnull' => $tableColumn['null'] !== 'YES',
'scale' => null,
'precision' => null,
'autoincrement' => (bool) (strpos($tableColumn['extra'], 'auto_increment') !== false),
'autoincrement' => strpos($tableColumn['extra'], 'auto_increment') !== false,
'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== ''
? $tableColumn['comment']
: null,
];
if ($scale !== null && $precision !== null) {
$options['scale'] = $scale;
$options['precision'] = $precision;
$options['scale'] = (int) $scale;
$options['precision'] = (int) $precision;
}
$column = new Column($tableColumn['field'], Type::getType($type), $options);
......@@ -206,6 +211,45 @@ class MySqlSchemaManager extends AbstractSchemaManager
return $column;
}
/**
* Return Doctrine/Mysql-compatible column default values for MariaDB 10.2.7+ servers.
*
* - Since MariaDb 10.2.7 column defaults stored in information_schema are now quoted
* to distinguish them from expressions (see MDEV-10134).
* - CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE are stored in information_schema
* as current_timestamp(), currdate(), currtime()
* - Quoted 'NULL' is not enforced by Maria, it is technically possible to have
* null in some circumstances (see https://jira.mariadb.org/browse/MDEV-14053)
* - \' is always stored as '' in information_schema (normalized)
*
* @link https://mariadb.com/kb/en/library/information-schema-columns-table/
* @link https://jira.mariadb.org/browse/MDEV-13132
*
* @param null|string $columnDefault default value as stored in information_schema for MariaDB >= 10.2.7
*/
private function getMariaDb1027ColumnDefault(MariaDb1027Platform $platform, ?string $columnDefault) : ?string
{
if ($columnDefault === 'NULL' || $columnDefault === null) {
return null;
}
if ($columnDefault[0] === "'") {
return stripslashes(
str_replace("''", "'",
preg_replace('/^\'(.*)\'$/', '$1', $columnDefault)
)
);
}
switch ($columnDefault) {
case 'current_timestamp()':
return $platform->getCurrentTimestampSQL();
case 'curdate()':
return $platform->getCurrentDateSQL();
case 'curtime()':
return $platform->getCurrentTimeSQL();
}
return $columnDefault;
}
/**
* {@inheritdoc}
*/
......@@ -214,11 +258,11 @@ class MySqlSchemaManager extends AbstractSchemaManager
$list = [];
foreach ($tableForeignKeys as $value) {
$value = array_change_key_case($value, CASE_LOWER);
if (!isset($list[$value['constraint_name']])) {
if (!isset($value['delete_rule']) || $value['delete_rule'] == "RESTRICT") {
if ( ! isset($list[$value['constraint_name']])) {
if ( ! isset($value['delete_rule']) || $value['delete_rule'] === "RESTRICT") {
$value['delete_rule'] = null;
}
if (!isset($value['update_rule']) || $value['update_rule'] == "RESTRICT") {
if ( ! isset($value['update_rule']) || $value['update_rule'] === "RESTRICT") {
$value['update_rule'] = null;
}
......@@ -231,15 +275,17 @@ class MySqlSchemaManager extends AbstractSchemaManager
'onUpdate' => $value['update_rule'],
];
}
$list[$value['constraint_name']]['local'][] = $value['column_name'];
$list[$value['constraint_name']]['local'][] = $value['column_name'];
$list[$value['constraint_name']]['foreign'][] = $value['referenced_column_name'];
}
$result = [];
foreach ($list as $constraint) {
$result[] = new ForeignKeyConstraint(
array_values($constraint['local']), $constraint['foreignTable'],
array_values($constraint['foreign']), $constraint['name'],
array_values($constraint['local']),
$constraint['foreignTable'],
array_values($constraint['foreign']),
$constraint['name'],
[
'onDelete' => $constraint['onDelete'],
'onUpdate' => $constraint['onUpdate'],
......
......@@ -7,7 +7,6 @@ use Doctrine\DBAL\Driver\DriverException;
use Doctrine\DBAL\Driver\ExceptionConverterDriver;
use Doctrine\DBAL\VersionAwarePlatformDriver;
use Doctrine\Tests\DbalTestCase;
use Throwable;
abstract class AbstractDriverTest extends DbalTestCase
{
......@@ -111,19 +110,29 @@ abstract class AbstractDriverTest extends DbalTestCase
$data = $this->getDatabasePlatformsForVersions();
if (empty($data)) {
$this->fail(
self::assertNotEmpty(
$data,
sprintf(
'No test data found for test %s. You have to return test data from %s.',
get_class($this) . '::' . __FUNCTION__,
get_class($this) . '::getDatabasePlatformsForVersions'
)
);
foreach ($data as $item) {
$generatedVersion = get_class($this->driver->createDatabasePlatformForVersion($item[0]));
self::assertSame(
$item[1],
$generatedVersion,
sprintf(
'No test data found for test %s. You have to return test data from %s.',
get_class($this) . '::' . __FUNCTION__,
get_class($this) . '::getDatabasePlatformsForVersions'
'Expected platform for version "%s" should be "%s", "%s" given',
$item[0],
$item[1],
$generatedVersion
)
);
}
foreach ($data as $item) {
self::assertSame($item[1], get_class($this->driver->createDatabasePlatformForVersion($item[0])));
}
}
/**
......
......@@ -3,6 +3,8 @@
namespace Doctrine\Tests\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Platforms\MariaDb1027Platform;
use Doctrine\DBAL\Platforms\MySQL57Platform;
use Doctrine\DBAL\Platforms\MySqlPlatform;
use Doctrine\DBAL\Schema\MySqlSchemaManager;
......@@ -52,20 +54,24 @@ class AbstractMySQLDriverTest extends AbstractDriverTest
return new MySqlSchemaManager($connection);
}
protected function getDatabasePlatformsForVersions()
protected function getDatabasePlatformsForVersions() : array
{
return array(
array('5.6.9', 'Doctrine\DBAL\Platforms\MySqlPlatform'),
array('5.7', 'Doctrine\DBAL\Platforms\MySQL57Platform'),
array('5.7.0', 'Doctrine\DBAL\Platforms\MySqlPlatform'),
array('5.7.8', 'Doctrine\DBAL\Platforms\MySqlPlatform'),
array('5.7.9', 'Doctrine\DBAL\Platforms\MySQL57Platform'),
array('5.7.10', 'Doctrine\DBAL\Platforms\MySQL57Platform'),
array('6', 'Doctrine\DBAL\Platforms\MySQL57Platform'),
array('10.0.15-MariaDB-1~wheezy', 'Doctrine\DBAL\Platforms\MySqlPlatform'),
array('10.1.2a-MariaDB-a1~lenny-log', 'Doctrine\DBAL\Platforms\MySqlPlatform'),
array('5.5.40-MariaDB-1~wheezy', 'Doctrine\DBAL\Platforms\MySqlPlatform'),
);
return [
['5.6.9', MySqlPlatform::class],
['5.7', MySQL57Platform::class],
['5.7.0', MySqlPlatform::class],
['5.7.8', MySqlPlatform::class],
['5.7.9', MySQL57Platform::class],
['5.7.10', MySQL57Platform::class],
['6', MySQL57Platform::class],
['10.0.15-MariaDB-1~wheezy', MySqlPlatform::class],
['5.5.5-10.1.25-MariaDB', MySqlPlatform::class],
['10.1.2a-MariaDB-a1~lenny-log', MySqlPlatform::class],
['5.5.40-MariaDB-1~wheezy', MySqlPlatform::class],
['5.5.5-MariaDB-10.2.8+maria~xenial-log', MariaDb1027Platform::class],
['10.2.8-MariaDB-10.2.8+maria~xenial-log', MariaDb1027Platform::class],
['10.2.8-MariaDB-1~lenny-log', MariaDb1027Platform::class]
];
}
protected function getExceptionConversionData()
......
......@@ -35,12 +35,12 @@ class DriverTest extends PDOMySQLDriverTest
return new DrizzleSchemaManager($connection);
}
protected function getDatabasePlatformsForVersions()
protected function getDatabasePlatformsForVersions() : array
{
return array(
array('foo', 'Doctrine\DBAL\Platforms\DrizzlePlatform'),
array('bar', 'Doctrine\DBAL\Platforms\DrizzlePlatform'),
array('baz', 'Doctrine\DBAL\Platforms\DrizzlePlatform'),
);
return [
['foo', 'Doctrine\DBAL\Platforms\DrizzlePlatform'],
['bar', 'Doctrine\DBAL\Platforms\DrizzlePlatform'],
['baz', 'Doctrine\DBAL\Platforms\DrizzlePlatform'],
];
}
}
......@@ -2,11 +2,13 @@
namespace Doctrine\Tests\DBAL\Functional\Schema;
use Doctrine\DBAL\Platforms\MariaDb1027Platform;
use Doctrine\DBAL\Platforms\MySqlPlatform;
use Doctrine\DBAL\Schema\Comparator;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Types\Type;
use Doctrine\Tests\Types\MySqlPointType;
class MySqlSchemaManagerTest extends SchemaManagerFunctionalTestCase
{
......@@ -14,8 +16,8 @@ class MySqlSchemaManagerTest extends SchemaManagerFunctionalTestCase
{
parent::setUp();
if (!Type::hasType('point')) {
Type::addType('point', 'Doctrine\Tests\Types\MySqlPointType');
if ( ! Type::hasType('point')) {
Type::addType('point', MySqlPointType::class);
}
}
......@@ -155,11 +157,17 @@ class MySqlSchemaManagerTest extends SchemaManagerFunctionalTestCase
*/
public function testDoesNotPropagateDefaultValuesForUnsupportedColumnTypes()
{
if ($this->_sm->getDatabasePlatform() instanceof MariaDb1027Platform) {
$this->markTestSkipped(
'MariaDb102Platform supports default values for BLOB and TEXT columns and will propagate values'
);
}
$table = new Table("text_blob_default_value");
$table->addColumn('def_text', 'text', array('default' => 'def'));
$table->addColumn('def_text_null', 'text', array('notnull' => false, 'default' => 'def'));
$table->addColumn('def_blob', 'blob', array('default' => 'def'));
$table->addColumn('def_blob_null', 'blob', array('notnull' => false, 'default' => 'def'));
$table->addColumn('def_text', 'text', ['default' => 'def']);
$table->addColumn('def_text_null', 'text', ['notnull' => false, 'default' => 'def']);
$table->addColumn('def_blob', 'blob', ['default' => 'def']);
$table->addColumn('def_blob_null', 'blob', ['notnull' => false, 'default' => 'def']);
$this->_sm->dropAndCreateTable($table);
......@@ -325,4 +333,148 @@ class MySqlSchemaManagerTest extends SchemaManagerFunctionalTestCase
self::assertFalse($columns['col']->getUnsigned());
self::assertTrue($columns['col_unsigned']->getUnsigned());
}
public function testJsonColumnType() : void
{
$table = new Table('test_mysql_json');
$table->addColumn('col_json', 'json');
$this->_sm->dropAndCreateTable($table);
$columns = $this->_sm->listTableColumns('test_mysql_json');
self::assertSame(TYPE::JSON, $columns['col_json']->getType()->getName());
}
public function testColumnDefaultCurrentTimestamp() : void
{
$platform = $this->_sm->getDatabasePlatform();
$table = new Table("test_column_defaults_current_timestamp");
$currentTimeStampSql = $platform->getCurrentTimestampSQL();
$table->addColumn('col_datetime', 'datetime', ['notnull' => true, 'default' => $currentTimeStampSql]);
$table->addColumn('col_datetime_nullable', 'datetime', ['default' => $currentTimeStampSql]);
$this->_sm->dropAndCreateTable($table);
$onlineTable = $this->_sm->listTableDetails("test_column_defaults_current_timestamp");
self::assertSame($currentTimeStampSql, $onlineTable->getColumn('col_datetime')->getDefault());
self::assertSame($currentTimeStampSql, $onlineTable->getColumn('col_datetime_nullable')->getDefault());
$comparator = new Comparator();
$diff = $comparator->diffTable($table, $onlineTable);
self::assertFalse($diff, "Tables should be identical with column defaults.");
}
public function testColumnDefaultsAreValid()
{
$table = new Table("test_column_defaults_are_valid");
$currentTimeStampSql = $this->_sm->getDatabasePlatform()->getCurrentTimestampSQL();
$table->addColumn('col_datetime', 'datetime', ['default' => $currentTimeStampSql]);
$table->addColumn('col_datetime_null', 'datetime', ['notnull' => false, 'default' => null]);
$table->addColumn('col_int', 'integer', ['default' => 1]);
$table->addColumn('col_neg_int', 'integer', ['default' => -1]);
$table->addColumn('col_string', 'string', ['default' => 'A']);
$table->addColumn('col_decimal', 'decimal', ['scale' => 3, 'precision' => 6, 'default' => -2.3]);
$table->addColumn('col_date', 'date', ['default' => '2012-12-12']);
$this->_sm->dropAndCreateTable($table);
$this->_conn->executeUpdate(
"INSERT INTO test_column_defaults_are_valid () VALUES()"
);
$row = $this->_conn->fetchAssoc(
'SELECT *, DATEDIFF(CURRENT_TIMESTAMP(), col_datetime) as diff_seconds FROM test_column_defaults_are_valid'
);
self::assertInstanceOf(\DateTime::class, \DateTime::createFromFormat('Y-m-d H:i:s', $row['col_datetime']));
self::assertNull($row['col_datetime_null']);
self::assertSame('2012-12-12', $row['col_date']);
self::assertSame('A', $row['col_string']);
self::assertEquals(1, $row['col_int']);
self::assertEquals(-1, $row['col_neg_int']);
self::assertEquals('-2.300', $row['col_decimal']);
self::assertLessThan(5, $row['diff_seconds']);
}
/**
* MariaDB 10.2+ does support CURRENT_TIME and CURRENT_DATE as
* column default values for time and date columns.
* (Not supported on Mysql as of 5.7.19)
*
* Note that MariaDB 10.2+, when storing default in information_schema,
* silently change CURRENT_TIMESTAMP as 'current_timestamp()',
* CURRENT_TIME as 'currtime()' and CURRENT_DATE as 'currdate()'.
* This test also ensure proper aliasing to not trigger a table diff.
*/
public function testColumnDefaultValuesCurrentTimeAndDate() : void
{
if ( ! $this->_sm->getDatabasePlatform() instanceof MariaDb1027Platform) {
$this->markTestSkipped('Only relevant for MariaDb102Platform.');
}
$platform = $this->_sm->getDatabasePlatform();
$table = new Table("test_column_defaults_current_time_and_date");
$currentTimestampSql = $platform->getCurrentTimestampSQL();
$currentTimeSql = $platform->getCurrentTimeSQL();
$currentDateSql = $platform->getCurrentDateSQL();
$table->addColumn('col_datetime', 'datetime', ['default' => $currentTimestampSql]);
$table->addColumn('col_date', 'date', ['default' => $currentDateSql]);
$table->addColumn('col_time', 'time', ['default' => $currentTimeSql]);
$this->_sm->dropAndCreateTable($table);
$onlineTable = $this->_sm->listTableDetails("test_column_defaults_current_time_and_date");
self::assertSame($currentTimestampSql, $onlineTable->getColumn('col_datetime')->getDefault());
self::assertSame($currentDateSql, $onlineTable->getColumn('col_date')->getDefault());
self::assertSame($currentTimeSql, $onlineTable->getColumn('col_time')->getDefault());
$comparator = new Comparator();
$diff = $comparator->diffTable($table, $onlineTable);
self::assertFalse($diff, "Tables should be identical with column defauts time and date.");
}
/**
* Ensure default values (un-)escaping is properly done by mysql platforms.
* The test is voluntarily relying on schema introspection due to current
* doctrine limitations. Once #2850 is landed, this test can be removed.
* @see https://dev.mysql.com/doc/refman/5.7/en/string-literals.html
*/
public function testEnsureDefaultsAreUnescapedFromSchemaIntrospection() : void
{
$platform = $this->_sm->getDatabasePlatform();
$this->_conn->query('DROP TABLE IF EXISTS test_column_defaults_with_create');
$escapeSequences = [
"\\0", // An ASCII NUL (X'00') character
"\\'", "''", // Single quote
'\\"', '""', // Double quote
'\\b', // A backspace character
'\\n', // A new-line character
'\\r', // A carriage return character
'\\t', // A tab character
'\\Z', // ASCII 26 (Control+Z)
'\\\\', // A backslash (\) character
'\\%', // A percent (%) character
'\\_', // An underscore (_) character
];
$default = implode('+', $escapeSequences);
$sql = "CREATE TABLE test_column_defaults_with_create(
col1 VARCHAR(255) NULL DEFAULT {$platform->quoteStringLiteral($default)}
)";
$this->_conn->query($sql);
$onlineTable = $this->_sm->listTableDetails("test_column_defaults_with_create");
self::assertSame($default, $onlineTable->getColumn('col1')->getDefault());
}
}
......@@ -526,7 +526,7 @@ abstract class AbstractPlatformTestCase extends \Doctrine\Tests\DbalTestCase
{
// non-timestamp value will get single quotes
$field = array(
'type' => 'string',
'type' => Type::getType('string'),
'default' => 'non_timestamp'
);
......
<?php
namespace Doctrine\Tests\DBAL\Platforms;
use Doctrine\DBAL\Platforms\MariaDb1027Platform;
use Doctrine\DBAL\Schema\Comparator;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Types\Type;
class MariaDb1027PlatformTest extends AbstractMySQLPlatformTestCase
{
/**
* {@inheritdoc}
*/
public function createPlatform() : MariaDb1027Platform
{
return new MariaDb1027Platform();
}
public function testHasNativeJsonType() : void
{
self::assertFalse($this->_platform->hasNativeJsonType());
}
/**
* From MariaDB 10.2.7, JSON type is an alias to LONGTEXT
* @link https://mariadb.com/kb/en/library/json-data-type/
*/
public function testReturnsJsonTypeDeclarationSQL() : void
{
self::assertSame('LONGTEXT', $this->_platform->getJsonTypeDeclarationSQL([]));
}
public function testInitializesJsonTypeMapping() : void
{
self::assertTrue($this->_platform->hasDoctrineTypeMappingFor('json'));
self::assertSame(Type::JSON, $this->_platform->getDoctrineTypeMapping('json'));
}
/**
* Overrides and skips AbstractMySQLPlatformTestCase test regarding propagation
* of unsupported default values for Blob and Text columns.
*
* @see AbstractMySQLPlatformTestCase::testDoesNotPropagateDefaultValuesForUnsupportedColumnTypes()
*/
public function testDoesNotPropagateDefaultValuesForUnsupportedColumnTypes() : void
{
$this->markTestSkipped('MariaDB102Platform support propagation of default values for BLOB and TEXT columns');
}
}
<?xml version="1.0" encoding="utf-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.8/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="../../vendor/autoload.php"
>
<php>
<ini name="error_reporting" value="-1" />
<var name="db_type" value="mysqli"/>
<var name="db_host" value="localhost" />
<var name="db_username" value="travis" />
<var name="db_password" value="" />
<var name="db_name" value="doctrine_tests" />
<var name="db_port" value="3306"/>
<var name="tmpdb_type" value="mysqli"/>
<var name="tmpdb_host" value="localhost" />
<var name="tmpdb_username" value="travis" />
<var name="tmpdb_password" value="" />
<var name="tmpdb_port" value="3306"/>
</php>
<testsuites>
<testsuite name="Doctrine DBAL Test Suite">
<directory>../Doctrine/Tests/DBAL</directory>
</testsuite>
</testsuites>
<groups>
<exclude>
<group>performance</group>
<group>locking_functional</group>
</exclude>
</groups>
</phpunit>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment