From baa454c9be0173607de18f4ea76ac3e60249246c Mon Sep 17 00:00:00 2001 From: belgattitude Date: Tue, 26 Sep 2017 16:40:22 +0200 Subject: [PATCH 01/45] Updated mariadb 10.2 support --- .travis.yml | 36 ++- .../DBAL/Driver/AbstractMySQLDriver.php | 67 +++- .../DBAL/Driver/DrizzlePDOMySql/Driver.php | 3 +- .../DBAL/Driver/Mysqli/MysqliConnection.php | 9 + .../DBAL/Platforms/AbstractPlatform.php | 12 +- .../Platforms/Keywords/MariaDb102Keywords.php | 285 ++++++++++++++++++ .../DBAL/Platforms/MariaDb1027Platform.php | 80 +++++ lib/Doctrine/DBAL/Platforms/MySqlPlatform.php | 19 +- .../DBAL/Schema/MySqlSchemaManager.php | 65 +++- .../Tests/DBAL/Driver/AbstractDriverTest.php | 13 +- .../DBAL/Driver/AbstractMySQLDriverTest.php | 27 +- .../Schema/MySqlSchemaManagerTest.php | 264 ++++++++++++++++ .../AbstractMySQLPlatformTestCase.php | 16 + .../Platforms/AbstractPlatformTestCase.php | 2 +- .../Platforms/MariaDb1027PlatformTest.php | 62 ++++ tests/travis/mariadb.mysqli.travis.xml | 38 +++ 16 files changed, 952 insertions(+), 46 deletions(-) create mode 100644 lib/Doctrine/DBAL/Platforms/Keywords/MariaDb102Keywords.php create mode 100644 lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php create mode 100644 tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php create mode 100644 tests/travis/mariadb.mysqli.travis.xml diff --git a/.travis.yml b/.travis.yml index 4facbe8df8a..b480e9cf066 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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,40 @@ 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 diff --git a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php index a06d2c0ab67..1570036be71 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php @@ -22,6 +22,8 @@ use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver; use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Platforms\MariaDb1027Platform; use Doctrine\DBAL\Platforms\MySQL57Platform; use Doctrine\DBAL\Platforms\MySqlPlatform; use Doctrine\DBAL\Schema\MySqlSchemaManager; @@ -123,35 +125,72 @@ public function convertException($message, DriverException $exception) /** * {@inheritdoc} + * + * @return AbstractPlatform|MariaDb1027Platform|MySQL57Platform|MySqlPlatform + * @throws DBALException */ - public function createDatabasePlatformForVersion($version) + public function createDatabasePlatformForVersion($version): AbstractPlatform { - if ( ! preg_match('/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $version, $versionParts)) { + if (false !== stripos($version, 'mariadb')) { + $versionNumber = $this->getMariaDbMysqlVersionNumber($version); + if (version_compare($versionNumber, '10.2.7', '>=')) { + return new MariaDb1027Platform(); + } + } else { + $versionNumber = $this->getOracleMysqlVersionNumber($version); + if (version_compare($versionNumber, '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\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $versionString, $versionParts)) { throw DBALException::invalidPlatformVersionSpecified( - $version, + $versionString, '..' ); } - - if (false !== stripos($version, 'mariadb')) { - return $this->getDatabasePlatform(); - } - $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 + { + $version = str_replace('5.5.5-', '', $versionString); + + if (!preg_match('/^(mariadb-)?(?P\d+)\.(?P\d+)\.(?P\d+)/', strtolower($version), $versionParts)) { + throw DBALException::invalidPlatformVersionSpecified( + $version, + '(mariadb-)?..' + ); } - return $this->getDatabasePlatform(); + return $versionParts['major'] . '.' . $versionParts['minor'] . '.' . $versionParts['patch']; } /** diff --git a/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php b/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php index 547fdb79570..a78306a764f 100644 --- a/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php +++ b/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php @@ -19,6 +19,7 @@ namespace Doctrine\DBAL\Driver\DrizzlePDOMySql; +use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\DrizzlePlatform; use Doctrine\DBAL\Schema\DrizzleSchemaManager; @@ -47,7 +48,7 @@ public function connect(array $params, $username = null, $password = null, array /** * {@inheritdoc} */ - public function createDatabasePlatformForVersion($version) + public function createDatabasePlatformForVersion($version): AbstractPlatform { return $this->getDatabasePlatform(); } diff --git a/lib/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php b/lib/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php index 9d30b968620..79955a0c046 100644 --- a/lib/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php +++ b/lib/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php @@ -94,9 +94,18 @@ public function getWrappedResourceHandle() /** * {@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); diff --git a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php index 1822a56335a..389a44fba9b 100644 --- a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php @@ -2281,32 +2281,32 @@ public function getDefaultValueDeclarationSQL($field) $default = $field['default']; if ( ! isset($field['type'])) { - return " DEFAULT '" . $default . "'"; + return ' DEFAULT ' . $this->quoteStringLiteral($default); } $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) { return " DEFAULT '" . $this->convertBooleans($default) . "'"; } - return " DEFAULT '" . $default . "'"; + return ' DEFAULT ' . $this->quoteStringLiteral($default); } /** diff --git a/lib/Doctrine/DBAL/Platforms/Keywords/MariaDb102Keywords.php b/lib/Doctrine/DBAL/Platforms/Keywords/MariaDb102Keywords.php new file mode 100644 index 00000000000..70cbf3af38b --- /dev/null +++ b/lib/Doctrine/DBAL/Platforms/Keywords/MariaDb102Keywords.php @@ -0,0 +1,285 @@ +. + */ + +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', + ]; + } +} diff --git a/lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php b/lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php new file mode 100644 index 00000000000..0ba6d91b7ce --- /dev/null +++ b/lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\DBAL\Platforms; + +use Doctrine\DBAL\Types\BlobType; +use Doctrine\DBAL\Types\StringType; +use Doctrine\DBAL\Types\TextType; +use Doctrine\DBAL\Types\Type; + +/** + * Provides the behavior, features and SQL dialect of the MariaDB 10.2 (10.2.7 GA) database platform. + * + * @author Vanvelthem Sébastien + * @link www.doctrine-project.org + */ +final class MariaDb1027Platform extends MySqlPlatform +{ + /** + * {@inheritdoc} + */ + public function hasNativeJsonType(): bool + { + return true; + } + + /** + * {@inheritdoc} + * @link https://mariadb.com/kb/en/library/json-data-type/ + */ + public function getJsonTypeDeclarationSQL(array $field): string + { + return 'JSON'; + } + + /** + * {@inheritdoc} + */ + protected function getReservedKeywordsClass(): string + { + return Keywords\MariaDb102Keywords::class; + } + + /** + * {@inheritdoc} + */ + protected function initializeDoctrineTypeMappings(): void + { + parent::initializeDoctrineTypeMappings(); + + $this->doctrineTypeMapping['json'] = Type::JSON; + } + + /** + * @inheritdoc + * + * Since MariaDB 10.2.1 blob and text columns can have a default value. + * @link https://mariadb.com/kb/en/library/blob-and-text-data-types/ + */ + protected function isDefaultValueSupportedForType(Type $field): bool + { + return true; + } +} diff --git a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php index 5d2b6339dac..347b2cc2264 100644 --- a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php @@ -26,6 +26,7 @@ use Doctrine\DBAL\Schema\TableDiff; use Doctrine\DBAL\Types\BlobType; use Doctrine\DBAL\Types\TextType; +use Doctrine\DBAL\Types\Type; /** * The MySqlPlatform provides the behavior, features and SQL dialect of the @@ -459,13 +460,24 @@ protected function _getCreateTableSQL($tableName, array $columns, array $options return $sql; } + /** + * Tells whether a field type supports declaration of a default value. + * + * MySQL (as of 5.7.19) does not support default values for Blob and Text + * columns while MariaDB 10.2.1 does. + */ + protected function isDefaultValueSupportedForType(Type $field): bool + { + return !($field instanceof TextType || $field instanceof BlobType); + } + /** * {@inheritdoc} */ public function getDefaultValueDeclarationSQL($field) { - // Unset the default value if the given field definition does not allow default values. - if ($field['type'] instanceof TextType || $field['type'] instanceof BlobType) { + // Unset the default value if the given field type does not allow default values. + if (!$this->isDefaultValueSupportedForType($field['type'])) { $field['default'] = null; } @@ -583,7 +595,8 @@ public function getAlterTableSQL(TableDiff $diff) // Don't propagate default value changes for unsupported column types. if ($columnDiff->hasChanged('default') && count($columnDiff->changedProperties) === 1 && - ($columnArray['type'] instanceof TextType || $columnArray['type'] instanceof BlobType) + !$this->isDefaultValueSupportedForType($columnArray['type']) + //($columnArray['type'] instanceof TextType || $columnArray['type'] instanceof BlobType) ) { continue; } diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index 9de4b8e330f..26d11b1887e 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -19,6 +19,7 @@ namespace Doctrine\DBAL\Schema; +use Doctrine\DBAL\Platforms\MariaDb1027Platform; use Doctrine\DBAL\Platforms\MySqlPlatform; use Doctrine\DBAL\Types\Type; @@ -67,7 +68,7 @@ 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; @@ -176,17 +177,25 @@ protected function _getPortableTableColumnDefinition($tableColumn) break; } - $length = ((int) $length == 0) ? null : (int) $length; + $length = $length !== null ? (int) $length : null; + + $isNotNull = $tableColumn['null'] !== 'YES'; + + if ($this->_platform instanceof MariaDb1027Platform) { + $columnDefault = $this->getMariaDb1027ColumnDefault($this->_platform, $tableColumn['default'] ?? null); + } else { + $columnDefault = (isset($tableColumn['default'])) ? $tableColumn['default'] : null; + } $options = [ 'length' => $length, - 'unsigned' => (bool) (strpos($tableColumn['type'], 'unsigned') !== false), + 'unsigned' => strpos($tableColumn['type'], 'unsigned') !== false, 'fixed' => (bool) $fixed, - 'default' => isset($tableColumn['default']) ? $tableColumn['default'] : null, - 'notnull' => (bool) ($tableColumn['null'] != 'YES'), + 'default' => $columnDefault, + 'notnull' => $isNotNull, '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, @@ -206,6 +215,46 @@ protected function _getPortableTableColumnDefinition($tableColumn) return $column; } + + /** + * Return column default value for MariaDB >= 10.2.7 servers that is + * compatible with existing doctrine mysql implementation (unquoted literals) + * + * Since 10.2.7: + * + * 1. Column defaults stored in information_schema are now quoted + * to distinguish them from expressions (see MDEV-10134 for a what is an expression). + * + * @link https://mariadb.com/kb/en/library/information-schema-columns-table/ + * @link https://jira.mariadb.org/browse/MDEV-13132 + * + * 2. Quoted string defaults use double single quotes as escaping character in information_schema. + * + * @links https://mariadb.com/kb/en/library/string-literals/ + * + * @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) { + $defaultValue = null; + } elseif (strpos($columnDefault, "'") === 0) { + $defaultValue = stripslashes( + str_replace("''", "'", + preg_replace('/^\'(.*)\'$/', '$1', $columnDefault) + ) + ); + } elseif ($columnDefault === 'current_timestamp()') { + $defaultValue = $platform->getCurrentTimestampSQL(); + } elseif ($columnDefault === 'curdate()') { + $defaultValue = $platform->getCurrentDateSQL(); + } elseif ($columnDefault === 'curtime()') { + $defaultValue = $platform->getCurrentTimeSQL(); + } else { + $defaultValue = $columnDefault; + } + return $defaultValue; + } + /** * {@inheritdoc} */ @@ -215,10 +264,10 @@ protected function _getPortableTableForeignKeysList($tableForeignKeys) 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($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; } diff --git a/tests/Doctrine/Tests/DBAL/Driver/AbstractDriverTest.php b/tests/Doctrine/Tests/DBAL/Driver/AbstractDriverTest.php index ed4dd21d0fa..740f2f4fc54 100644 --- a/tests/Doctrine/Tests/DBAL/Driver/AbstractDriverTest.php +++ b/tests/Doctrine/Tests/DBAL/Driver/AbstractDriverTest.php @@ -7,7 +7,6 @@ use Doctrine\DBAL\Driver\ExceptionConverterDriver; use Doctrine\DBAL\VersionAwarePlatformDriver; use Doctrine\Tests\DbalTestCase; -use Throwable; abstract class AbstractDriverTest extends DbalTestCase { @@ -122,7 +121,17 @@ public function testCreatesDatabasePlatformForVersion() } foreach ($data as $item) { - self::assertSame($item[1], get_class($this->driver->createDatabasePlatformForVersion($item[0]))); + self::assertSame($item[1], get_class($this->driver->createDatabasePlatformForVersion($item[0])), + sprintf( + "Expected platform for version %s should be '%s' %s", + $item[0], + $item[1], + ($this->driver instanceof VersionAwarePlatformDriver + ? 'expected: ' . get_class($this->driver->createDatabasePlatformForVersion($item[0])) + : 'see:' . $item[0] + ) + + )); } } diff --git a/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php b/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php index 87676a1920d..9813e06f1dd 100644 --- a/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php +++ b/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php @@ -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; @@ -55,16 +57,21 @@ protected function createSchemaManager(Connection $connection) protected function getDatabasePlatformsForVersions() { 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'), + array('5.6.9', MySqlPlatform::class), + array('5.7', MySQL57Platform::class), + array('5.7.0', MySqlPlatform::class), + array('5.7.8', MySqlPlatform::class), + array('5.7.9', MySQL57Platform::class), + array('5.7.10', MySQL57Platform::class), + array('6', MySQL57Platform::class), + array('10.0.15-MariaDB-1~wheezy', MySqlPlatform::class), + array('5.5.5-10.1.25-MariaDB', MySqlPlatform::class), + array('10.1.2a-MariaDB-a1~lenny-log', MySqlPlatform::class), + array('5.5.40-MariaDB-1~wheezy', MySqlPlatform::class), + array('5.5.40-MariaDB-1~wheezy', MySqlPlatform::class), + array('5.5.5-MariaDB-10.2.8+maria~xenial-log', MariaDb1027Platform::class), + array('10.2.8-MariaDB-10.2.8+maria~xenial-log', MariaDb1027Platform::class), + array('10.2.8-MariaDB-1~lenny-log', MariaDb1027Platform::class) ); } diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index d6070cc4c86..2950f3b84c9 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -2,14 +2,17 @@ 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\Schema\TableDiff; use Doctrine\DBAL\Types\Type; class MySqlSchemaManagerTest extends SchemaManagerFunctionalTestCase { + protected function setUp() { parent::setUp(); @@ -155,6 +158,10 @@ public function testDropPrimaryKeyWithAutoincrementColumn() */ public function testDoesNotPropagateDefaultValuesForUnsupportedColumnTypes() { + if ($this->_sm->getDatabasePlatform() instanceof MariaDb1027Platform) { + $this->markTestSkipped('MariaDb1027Platform 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')); @@ -186,6 +193,37 @@ public function testDoesNotPropagateDefaultValuesForUnsupportedColumnTypes() self::assertFalse($onlineTable->getColumn('def_blob_null')->getNotnull()); } + /** + * Since MariaDB 10.2.1, Blob and text columns can have a default value + * + * @link https://mariadb.com/kb/en/library/blob-and-text-data-types + */ + public function testDefaultValueSupportForBlobAndText() + { + if (!$this->_sm->getDatabasePlatform() instanceof MariaDb1027Platform) { + $this->markTestSkipped('Only MariaDb1027Platform supports default values for BLOB and TEXT columns'); + } + + $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')); + + $this->_sm->dropAndCreateTable($table); + + $onlineTable = $this->_sm->listTableDetails("text_blob_default_value"); + + self::assertSame('def', $onlineTable->getColumn('def_text')->getDefault()); + self::assertSame('def', $onlineTable->getColumn('def_text_null')->getDefault()); + self::assertSame('def', $onlineTable->getColumn('def_blob')->getDefault()); + self::assertSame('def', $onlineTable->getColumn('def_blob_null')->getDefault()); + + $comparator = new Comparator(); + + self::assertFalse($comparator->diffTable($table, $onlineTable)); + } + public function testColumnCollation() { $table = new Table('test_collation'); @@ -325,4 +363,230 @@ public function testListFloatTypeColumns() self::assertFalse($columns['col']->getUnsigned()); self::assertTrue($columns['col_unsigned']->getUnsigned()); } + + + /** + * As of MariaDB 10.2.7, nullable default values literals are always single quoted in + * information_schema. Non-nullable defaults behaviour is not affected. + * This test ensure accidental removal of double single encoded defaults for MariaDB >= 10.2.7. + * + * @link https://mariadb.com/kb/en/library/information-schema-columns-table/ + * @link https://dev.mysql.com/doc/refman/5.5/en/string-literals.html + */ + public function testColumnDefaultValuesDoubleQuoted(): void + { + + $table = new Table("test_column_default_values_double_quoted"); + $table->addColumn('string_nullable_quoted', 'string', ['notnull' => false, 'default' => 'NULL']); + $table->addColumn('string_nullable_double_quoted', 'string', ['notnull' => false, 'default' => "'NULL'"]); + + $table->addColumn('string_notnull_quoted', 'string', ['notnull' => true, 'default' => 'NULL']); + //$table->addColumn('string_notnull_double_quoted', 'string', ['notnull' => true, 'default' => "\\'NULL\\'"]); + + $this->_sm->dropAndCreateTable($table); + + $onlineTable = $this->_sm->listTableDetails("test_column_default_values_double_quoted"); + self::assertSame('NULL', $onlineTable->getColumn('string_nullable_quoted')->getDefault()); + + self::assertSame("'NULL'", $onlineTable->getColumn('string_nullable_double_quoted')->getDefault()); + self::assertSame("NULL", $onlineTable->getColumn('string_notnull_quoted')->getDefault()); + //self::assertSame("'NULL'", $onlineTable->getColumn('string_notnull_double_quoted')->getDefault()); + + $comparator = new Comparator(); + + $diff = $comparator->diffTable($table, $onlineTable); + $this->assertFalse($diff, "Tables should be identical with double quoted literals."); + + } + + + 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', ['notnull' => false, '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); + $this->assertFalse($diff, "Tables should be identical with column defaults."); + } + + /** + * Test default CURRENT_TIME and CURRENT_DATE as default values + * + * Note: MySQL (as of 5.7.19) does not support default value + * for DATE and TIME fields while MariaDB 10.2+ does + */ + public function testColumnDefaultCurrentTimeAndDate() + { + $platform = $this->_sm->getDatabasePlatform(); + + if (!$platform instanceof MariaDb1027Platform) { + $this->markTestSkipped('Currently only MariaDb1027Platform supports setting CURRENT_TIME and CURRENT_DATE default values.'); + } + + $table = new Table("test_column_defaults_current_time_and_date"); + + $currentTimeSql = $platform->getCurrentTimeSQL(); + $currentDateSql = $platform->getCurrentDateSQL(); + + $table->addColumn('col_date', 'date', ['notnull' => true, 'default' => $currentDateSql]); + $table->addColumn('col_time', 'time', ['notnull' => true, 'default' => $currentTimeSql]); + + $this->_sm->dropAndCreateTable($table); + + $onlineTable = $this->_sm->listTableDetails("test_column_defaults_current_time_and_date"); + + self::assertSame($currentDateSql, $onlineTable->getColumn('col_date')->getDefault()); + self::assertSame($currentTimeSql, $onlineTable->getColumn('col_time')->getDefault()); + + $comparator = new Comparator(); + + $diff = $comparator->diffTable($table, $onlineTable); + $this->assertFalse($diff, "Tables should be identical with column defaults."); + } + + + /** + * + * @link https://mariadb.com/kb/en/library/string-literals + */ + public function testColumnDefaultValuesEscaping(): void + { + $table = new Table("test_column_default_values_escaping"); + $table->addColumn('no_quotes', 'string', ['notnull' => false, 'default' => 'az']); + + $table->addColumn('backslash', 'string', ['notnull' => false, 'default' => 'a\\\z']); + $table->addColumn('repeated_single_quotes', 'string', ['notnull' => false, 'default' => "a''z"]); + + $this->_sm->dropAndCreateTable($table); + + $onlineTable = $this->_sm->listTableDetails("test_column_default_values_escaping"); + self::assertSame("az", $onlineTable->getColumn('no_quotes')->getDefault()); + self::assertSame('a\\\z', $onlineTable->getColumn('backslash')->getDefault()); + self::assertSame("a''z", $onlineTable->getColumn('repeated_single_quotes')->getDefault()); + + $comparator = new Comparator(); + + $diff = $comparator->diffTable($table, $onlineTable); + $this->assertFalse($diff, "Tables should be identical with values escape sequences."); + } + + public function testColumnDefaultsUsingDoctrineTable(): void + { + + $table = new Table("test_column_defaults_with_table"); + $table->addColumn('col0', 'integer', ['notnull' => false]); + $table->addColumn('col1', 'integer', ['notnull' => false, 'default' => null]); + $table->addColumn('col2', 'string', ['notnull' => false, 'default' => null]); + $table->addColumn('col3', 'string', ['notnull' => false, 'default' => 'NULL']); + $table->addColumn('col4', 'string', ['notnull' => false, 'default' => 'Hello world']); + $table->addColumn('col5', 'datetime', ['notnull' => false, 'default' => null]); + $table->addColumn('col6', 'decimal', ['scale' => 3, 'precision' => 6, 'notnull' => false, 'default' => -2.3]); + $table->addColumn('col7', 'date', ['notnull' => false, 'default' => '2012-12-12']); + $table->addColumn('col8', 'string', ['notnull' => true, 'default' => '']); + $table->addColumn('col9', 'integer', ['notnull' => false, 'default' => 0]); + $table->addColumn('col10', 'string', ['notnull' => false, 'default' => 'He"ll"o world']); + $table->addColumn('col11', 'string', ['notnull' => false, 'default' => '2012-12-12 23:59:59']); + //$table->addColumn('col12', 'string', ['notnull' => false, 'default' => 'He\\\'llo \\\world']); + + $table->addColumn('col20', 'string', ['notnull' => true, 'default' => 'CURRENT_TIMESTAMP()']); + $table->addColumn('col21', 'string', ['notnull' => false, 'default' => 'CURRENT_TIMESTAMP']); + + $this->_sm->dropAndCreateTable($table); + + $onlineTable = $this->_sm->listTableDetails("test_column_defaults_with_table"); + self::assertNull($onlineTable->getColumn('col0')->getDefault()); + self::assertNull($onlineTable->getColumn('col1')->getDefault()); + self::assertNull($onlineTable->getColumn('col2')->getDefault()); + self::assertEquals('NULL', $onlineTable->getColumn('col3')->getDefault()); + self::assertEquals('Hello world', $onlineTable->getColumn('col4')->getDefault()); + self::assertNull($onlineTable->getColumn('col5')->getDefault()); + self::assertEquals(-2.3, $onlineTable->getColumn('col6')->getDefault()); + self::assertEquals('2012-12-12', $onlineTable->getColumn('col7')->getDefault()); + self::assertTrue($onlineTable->getColumn('col8')->getNotnull()); + self::assertEquals('', $onlineTable->getColumn('col8')->getDefault()); + self::assertSame('0', $onlineTable->getColumn('col9')->getDefault()); + self::assertEquals('He"ll"o world', $onlineTable->getColumn('col10')->getDefault()); + self::assertEquals('2012-12-12 23:59:59', $onlineTable->getColumn('col11')->getDefault()); + //self::assertEquals('He\\\'llo \\world', $onlineTable->getColumn('col12')->getDefault()); + + // MariaDB 10.2 and MySQL 5.7 differences while storing default now() in information schema. + // MariaDB will always store "current_timestamp()", mysql "CURRENT_TIMESTAMP" + self::assertStringStartsWith('current_timestamp', strtolower($onlineTable->getColumn('col20')->getDefault())); + self::assertStringStartsWith('current_timestamp', strtolower($onlineTable->getColumn('col21')->getDefault())); + + $comparator = new Comparator(); + + $diff = $comparator->diffTable($table, $onlineTable); + self::assertFalse($diff, "Tables should be identical with column defaults."); + } + + /** + * Ensure that an existing table with quoted (literals) default values + * does not trigger a table change. + */ + public function testColumnDefaultsDoesNotTriggerADiff(): void + { + $this->_conn->query('DROP TABLE IF EXISTS test_column_defaults_no_diff'); + $sql = " + CREATE TABLE test_column_defaults_with_create ( + col1 VARCHAR(255) NULL DEFAULT 'O''Connor\'\"', + col2 VARCHAR(255) NULL DEFAULT '''A''' + ); + "; + + $this->_conn->query($sql); + $onlineTable = $this->_sm->listTableDetails("test_column_defaults_with_create"); + self::assertSame("O'Connor'\"", $onlineTable->getColumn('col1')->getDefault()); + self::assertSame("'A'", $onlineTable->getColumn('col2')->getDefault()); + + $table = new Table("test_column_defaults_no_diff"); + $table->addColumn('col1', 'string', ['notnull' => false, 'default' => "O'Connor'\""]); + $table->addColumn('col2', 'string', ['notnull' => false, 'default' => "'A'"]); + + $comparator = new Comparator(); + $diff = $comparator->diffTable($table, $onlineTable); + self::assertFalse($diff); + } + + /** + * MariaDB supports expressions as default values + * + * @link https://mariadb.com/kb/en/library/information-schema-columns-table/ + */ + public function testColumnDefaultExpressions(): void + { + $this->markTestSkipped('Setting an expression as a default value is not yet supported (WIP)'); + + if ($this->_sm->getDatabasePlatform() instanceof MariaDb1027Platform) { + + $table = new Table("test_column_default_expressions"); + + $table->addColumn('expression', 'string', ['notnull' => false, 'default' => "concat('A','B')"]); + + $this->_sm->dropAndCreateTable($table); + + $onlineTable = $this->_sm->listTableDetails("test_column_default_expressions"); + self::assertSame("concat('A','B')", $onlineTable->getColumn('expression')->getDefault()); + + $comparator = new Comparator(); + + $diff = $comparator->diffTable($table, $onlineTable); + $this->assertFalse($diff, "Tables should be identical with expression column defaults."); + } + } + } diff --git a/tests/Doctrine/Tests/DBAL/Platforms/AbstractMySQLPlatformTestCase.php b/tests/Doctrine/Tests/DBAL/Platforms/AbstractMySQLPlatformTestCase.php index 54f49354c52..19d7407a8c6 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/AbstractMySQLPlatformTestCase.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/AbstractMySQLPlatformTestCase.php @@ -8,6 +8,7 @@ use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Types\Type; abstract class AbstractMySQLPlatformTestCase extends AbstractPlatformTestCase { @@ -907,4 +908,19 @@ public function testListTableForeignKeysSQLEvaluatesDatabase() self::assertContains('bar', $sql); self::assertNotContains('DATABASE()', $sql); } + + public function testGetDefaultValueDeclarationSQLIsQuotedWithLiteral() + { + $field = [ + 'type' => Type::getType('string'), + 'default' => "'O'Connor said: \"Hello\" \ \r'" + ]; + + self::assertSame(sprintf( + " DEFAULT %s", + $this->_platform->quoteStringLiteral("'O'Connor said: \"Hello\" \ \r'") + ), + $this->_platform->getDefaultValueDeclarationSQL($field) + ); + } } diff --git a/tests/Doctrine/Tests/DBAL/Platforms/AbstractPlatformTestCase.php b/tests/Doctrine/Tests/DBAL/Platforms/AbstractPlatformTestCase.php index b8106c43fed..0484e13b3c3 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/AbstractPlatformTestCase.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/AbstractPlatformTestCase.php @@ -526,7 +526,7 @@ public function testGetDefaultValueDeclarationSQL() { // non-timestamp value will get single quotes $field = array( - 'type' => 'string', + 'type' => Type::getType('string'), 'default' => 'non_timestamp' ); diff --git a/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php new file mode 100644 index 00000000000..39a661bd33c --- /dev/null +++ b/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php @@ -0,0 +1,62 @@ +_platform->hasNativeJsonType()); + } + + public function testReturnsJsonTypeDeclarationSQL() + { + self::assertSame('JSON', $this->_platform->getJsonTypeDeclarationSQL([])); + } + + public function testInitializesJsonTypeMapping() + { + self::assertTrue($this->_platform->hasDoctrineTypeMappingFor('json')); + self::assertSame(Type::JSON, $this->_platform->getDoctrineTypeMapping('json')); + } + + /** + * Overrides AbstractMySQLPlatformTestCase::testDoesNotPropagateDefaultValuesForUnsupportedColumnTypes() + */ + public function testDoesNotPropagateDefaultValuesForUnsupportedColumnTypes() + { + $table = new Table("text_blob_default_value"); + + $table->addColumn('def_text', 'text', array('default' => 'def')); + $table->addColumn('def_blob', 'blob', array('default' => 'def')); + + self::assertSame( + array('CREATE TABLE text_blob_default_value (def_text LONGTEXT DEFAULT \'def\' NOT NULL, def_blob LONGBLOB DEFAULT \'def\' NOT NULL) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB'), + $this->_platform->getCreateTableSQL($table) + ); + + $diffTable = clone $table; + + $diffTable->changeColumn('def_text', array('default' => 'def')); + $diffTable->changeColumn('def_blob', array('default' => 'def')); + + $comparator = new Comparator(); + + self::assertFalse($comparator->diffTable($table, $diffTable)); + } + + +} diff --git a/tests/travis/mariadb.mysqli.travis.xml b/tests/travis/mariadb.mysqli.travis.xml new file mode 100644 index 00000000000..84bdac32c04 --- /dev/null +++ b/tests/travis/mariadb.mysqli.travis.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + ../Doctrine/Tests/DBAL + + + + + + performance + locking_functional + + + + From 166405127ea9ae9a6bf7eaf53640e7d82148d0af Mon Sep 17 00:00:00 2001 From: belgattitude Date: Tue, 26 Sep 2017 17:04:24 +0200 Subject: [PATCH 02/45] Removed unused use statement --- .../Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index 2950f3b84c9..26aef3a018d 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -7,7 +7,6 @@ use Doctrine\DBAL\Schema\Comparator; use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Table; -use Doctrine\DBAL\Schema\TableDiff; use Doctrine\DBAL\Types\Type; class MySqlSchemaManagerTest extends SchemaManagerFunctionalTestCase From 1f9deec384aeb0cd79c8655c9d80bb0e8ae5e898 Mon Sep 17 00:00:00 2001 From: belgattitude Date: Tue, 26 Sep 2017 17:59:15 +0200 Subject: [PATCH 03/45] Fixed $this->assert* --- .../DBAL/Functional/Schema/MySqlSchemaManagerTest.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index 26aef3a018d..9aabfd2e283 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -394,7 +394,7 @@ public function testColumnDefaultValuesDoubleQuoted(): void $comparator = new Comparator(); $diff = $comparator->diffTable($table, $onlineTable); - $this->assertFalse($diff, "Tables should be identical with double quoted literals."); + self::assertFalse($diff, "Tables should be identical with double quoted literals."); } @@ -419,7 +419,7 @@ public function testColumnDefaultCurrentTimestamp(): void $comparator = new Comparator(); $diff = $comparator->diffTable($table, $onlineTable); - $this->assertFalse($diff, "Tables should be identical with column defaults."); + self::assertFalse($diff, "Tables should be identical with column defaults."); } /** @@ -454,7 +454,7 @@ public function testColumnDefaultCurrentTimeAndDate() $comparator = new Comparator(); $diff = $comparator->diffTable($table, $onlineTable); - $this->assertFalse($diff, "Tables should be identical with column defaults."); + self::assertFalse($diff, "Tables should be identical with column defaults."); } @@ -480,7 +480,7 @@ public function testColumnDefaultValuesEscaping(): void $comparator = new Comparator(); $diff = $comparator->diffTable($table, $onlineTable); - $this->assertFalse($diff, "Tables should be identical with values escape sequences."); + self::assertFalse($diff, "Tables should be identical with values escape sequences."); } public function testColumnDefaultsUsingDoctrineTable(): void @@ -584,7 +584,7 @@ public function testColumnDefaultExpressions(): void $comparator = new Comparator(); $diff = $comparator->diffTable($table, $onlineTable); - $this->assertFalse($diff, "Tables should be identical with expression column defaults."); + self::assertFalse($diff, "Tables should be identical with expression column defaults."); } } From 4fff9761fb684b88e11d0cb0c9632468fac34941 Mon Sep 17 00:00:00 2001 From: belgattitude Date: Wed, 27 Sep 2017 03:08:31 +0200 Subject: [PATCH 04/45] Test JSON default values and refactoring --- .../DBAL/Driver/AbstractMySQLDriver.php | 15 +- ...027Platform.php => MariaDb102Platform.php} | 7 +- .../DBAL/Schema/MySqlSchemaManager.php | 22 +- .../DBAL/Driver/AbstractMySQLDriverTest.php | 8 +- .../Schema/MySqlSchemaManagerTest.php | 213 ++++++++++-------- .../Platforms/MariaDb1027PlatformTest.php | 62 ----- .../DBAL/Platforms/MariaDb102PlatformTest.php | 94 ++++++++ 7 files changed, 235 insertions(+), 186 deletions(-) rename lib/Doctrine/DBAL/Platforms/{MariaDb1027Platform.php => MariaDb102Platform.php} (93%) delete mode 100644 tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php create mode 100644 tests/Doctrine/Tests/DBAL/Platforms/MariaDb102PlatformTest.php diff --git a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php index 1570036be71..e8f86bed69b 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php @@ -23,9 +23,10 @@ use Doctrine\DBAL\Driver; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\AbstractPlatform; -use Doctrine\DBAL\Platforms\MariaDb1027Platform; +use Doctrine\DBAL\Platforms\MariaDb102Platform; use Doctrine\DBAL\Platforms\MySQL57Platform; use Doctrine\DBAL\Platforms\MySqlPlatform; +use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\MySqlSchemaManager; use Doctrine\DBAL\VersionAwarePlatformDriver; @@ -126,7 +127,7 @@ public function convertException($message, DriverException $exception) /** * {@inheritdoc} * - * @return AbstractPlatform|MariaDb1027Platform|MySQL57Platform|MySqlPlatform + * @return AbstractPlatform|MariaDb102Platform|MySQL57Platform|MySqlPlatform * @throws DBALException */ public function createDatabasePlatformForVersion($version): AbstractPlatform @@ -134,7 +135,7 @@ public function createDatabasePlatformForVersion($version): AbstractPlatform if (false !== stripos($version, 'mariadb')) { $versionNumber = $this->getMariaDbMysqlVersionNumber($version); if (version_compare($versionNumber, '10.2.7', '>=')) { - return new MariaDb1027Platform(); + return new MariaDb102Platform(); } } else { $versionNumber = $this->getOracleMysqlVersionNumber($version); @@ -196,7 +197,7 @@ private function getMariaDbMysqlVersionNumber(string $versionString): string /** * {@inheritdoc} */ - public function getDatabase(\Doctrine\DBAL\Connection $conn) + public function getDatabase(\Doctrine\DBAL\Connection $conn): ?string { $params = $conn->getParams(); @@ -209,16 +210,18 @@ public function getDatabase(\Doctrine\DBAL\Connection $conn) /** * {@inheritdoc} + * @return MySqlPlatform */ - public function getDatabasePlatform() + public function getDatabasePlatform(): AbstractPlatform { return new MySqlPlatform(); } /** * {@inheritdoc} + * @return MySqlSchemaManager */ - public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + public function getSchemaManager(\Doctrine\DBAL\Connection $conn): AbstractSchemaManager { return new MySqlSchemaManager($conn); } diff --git a/lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php b/lib/Doctrine/DBAL/Platforms/MariaDb102Platform.php similarity index 93% rename from lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php rename to lib/Doctrine/DBAL/Platforms/MariaDb102Platform.php index 0ba6d91b7ce..5688c3e9db1 100644 --- a/lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php +++ b/lib/Doctrine/DBAL/Platforms/MariaDb102Platform.php @@ -19,18 +19,17 @@ namespace Doctrine\DBAL\Platforms; -use Doctrine\DBAL\Types\BlobType; -use Doctrine\DBAL\Types\StringType; -use Doctrine\DBAL\Types\TextType; 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 +final class MariaDb102Platform extends MySqlPlatform { /** * {@inheritdoc} diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index 26d11b1887e..825f3d96daf 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -19,7 +19,7 @@ namespace Doctrine\DBAL\Schema; -use Doctrine\DBAL\Platforms\MariaDb1027Platform; +use Doctrine\DBAL\Platforms\MariaDb102Platform; use Doctrine\DBAL\Platforms\MySqlPlatform; use Doctrine\DBAL\Types\Type; @@ -181,7 +181,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) $isNotNull = $tableColumn['null'] !== 'YES'; - if ($this->_platform instanceof MariaDb1027Platform) { + if ($this->_platform instanceof MariaDb102Platform) { $columnDefault = $this->getMariaDb1027ColumnDefault($this->_platform, $tableColumn['default'] ?? null); } else { $columnDefault = (isset($tableColumn['default'])) ? $tableColumn['default'] : null; @@ -217,24 +217,20 @@ protected function _getPortableTableColumnDefinition($tableColumn) /** - * Return column default value for MariaDB >= 10.2.7 servers that is - * compatible with existing doctrine mysql implementation (unquoted literals) + * Return Doctrine/Mysql-compatible column default values for MariaDB 10.2.7+ servers. * - * Since 10.2.7: - * - * 1. Column defaults stored in information_schema are now quoted - * to distinguish them from expressions (see MDEV-10134 for a what is an expression). + * - Since MariaDb 10.2.7 column defaults stored in information_schema are now quoted + * to distinguish them from expressions (see MDEV-10134 for a what is an expression). + * - CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE are stored in information_schema + * as current_timestamp(), currdate(), currtime() + * - Literal escaping is normalized in information schema (store "''" instead of "\'") * * @link https://mariadb.com/kb/en/library/information-schema-columns-table/ * @link https://jira.mariadb.org/browse/MDEV-13132 * - * 2. Quoted string defaults use double single quotes as escaping character in information_schema. - * - * @links https://mariadb.com/kb/en/library/string-literals/ - * * @param null|string $columnDefault default value as stored in information_schema for MariaDB >= 10.2.7 */ - private function getMariaDb1027ColumnDefault(MariaDb1027Platform $platform, ?string $columnDefault): ?string { + private function getMariaDb1027ColumnDefault(MariaDb102Platform $platform, ?string $columnDefault): ?string { if ($columnDefault === 'NULL' || $columnDefault === null) { $defaultValue = null; } elseif (strpos($columnDefault, "'") === 0) { diff --git a/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php b/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php index 9813e06f1dd..a8204f54b55 100644 --- a/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php +++ b/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php @@ -3,7 +3,7 @@ namespace Doctrine\Tests\DBAL\Driver; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Platforms\MariaDb1027Platform; +use Doctrine\DBAL\Platforms\MariaDb102Platform; use Doctrine\DBAL\Platforms\MySQL57Platform; use Doctrine\DBAL\Platforms\MySqlPlatform; use Doctrine\DBAL\Schema\MySqlSchemaManager; @@ -69,9 +69,9 @@ protected function getDatabasePlatformsForVersions() array('10.1.2a-MariaDB-a1~lenny-log', MySqlPlatform::class), array('5.5.40-MariaDB-1~wheezy', MySqlPlatform::class), array('5.5.40-MariaDB-1~wheezy', MySqlPlatform::class), - array('5.5.5-MariaDB-10.2.8+maria~xenial-log', MariaDb1027Platform::class), - array('10.2.8-MariaDB-10.2.8+maria~xenial-log', MariaDb1027Platform::class), - array('10.2.8-MariaDB-1~lenny-log', MariaDb1027Platform::class) + array('5.5.5-MariaDB-10.2.8+maria~xenial-log', MariaDb102Platform::class), + array('10.2.8-MariaDB-10.2.8+maria~xenial-log', MariaDb102Platform::class), + array('10.2.8-MariaDB-1~lenny-log', MariaDb102Platform::class) ); } diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index 9aabfd2e283..1039f4b25bc 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -2,12 +2,13 @@ namespace Doctrine\Tests\DBAL\Functional\Schema; -use Doctrine\DBAL\Platforms\MariaDb1027Platform; +use Doctrine\DBAL\Platforms\MariaDb102Platform; 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 { @@ -17,7 +18,7 @@ protected function setUp() parent::setUp(); if (!Type::hasType('point')) { - Type::addType('point', 'Doctrine\Tests\Types\MySqlPointType'); + Type::addType('point', MySqlPointType::class); } } @@ -157,15 +158,15 @@ public function testDropPrimaryKeyWithAutoincrementColumn() */ public function testDoesNotPropagateDefaultValuesForUnsupportedColumnTypes() { - if ($this->_sm->getDatabasePlatform() instanceof MariaDb1027Platform) { - $this->markTestSkipped('MariaDb1027Platform supports default values for BLOB and TEXT columns and will propagate values'); + if ($this->_sm->getDatabasePlatform() instanceof MariaDb102Platform) { + $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); @@ -192,37 +193,6 @@ public function testDoesNotPropagateDefaultValuesForUnsupportedColumnTypes() self::assertFalse($onlineTable->getColumn('def_blob_null')->getNotnull()); } - /** - * Since MariaDB 10.2.1, Blob and text columns can have a default value - * - * @link https://mariadb.com/kb/en/library/blob-and-text-data-types - */ - public function testDefaultValueSupportForBlobAndText() - { - if (!$this->_sm->getDatabasePlatform() instanceof MariaDb1027Platform) { - $this->markTestSkipped('Only MariaDb1027Platform supports default values for BLOB and TEXT columns'); - } - - $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')); - - $this->_sm->dropAndCreateTable($table); - - $onlineTable = $this->_sm->listTableDetails("text_blob_default_value"); - - self::assertSame('def', $onlineTable->getColumn('def_text')->getDefault()); - self::assertSame('def', $onlineTable->getColumn('def_text_null')->getDefault()); - self::assertSame('def', $onlineTable->getColumn('def_blob')->getDefault()); - self::assertSame('def', $onlineTable->getColumn('def_blob_null')->getDefault()); - - $comparator = new Comparator(); - - self::assertFalse($comparator->diffTable($table, $onlineTable)); - } - public function testColumnCollation() { $table = new Table('test_collation'); @@ -374,13 +344,12 @@ public function testListFloatTypeColumns() */ public function testColumnDefaultValuesDoubleQuoted(): void { - $table = new Table("test_column_default_values_double_quoted"); $table->addColumn('string_nullable_quoted', 'string', ['notnull' => false, 'default' => 'NULL']); $table->addColumn('string_nullable_double_quoted', 'string', ['notnull' => false, 'default' => "'NULL'"]); $table->addColumn('string_notnull_quoted', 'string', ['notnull' => true, 'default' => 'NULL']); - //$table->addColumn('string_notnull_double_quoted', 'string', ['notnull' => true, 'default' => "\\'NULL\\'"]); + $table->addColumn('string_notnull_double_quoted', 'string', ['notnull' => true, 'default' => "\\'NULL\\'"]); $this->_sm->dropAndCreateTable($table); @@ -389,16 +358,14 @@ public function testColumnDefaultValuesDoubleQuoted(): void self::assertSame("'NULL'", $onlineTable->getColumn('string_nullable_double_quoted')->getDefault()); self::assertSame("NULL", $onlineTable->getColumn('string_notnull_quoted')->getDefault()); - //self::assertSame("'NULL'", $onlineTable->getColumn('string_notnull_double_quoted')->getDefault()); + self::assertSame("\\'NULL\\'", $onlineTable->getColumn('string_notnull_double_quoted')->getDefault()); $comparator = new Comparator(); $diff = $comparator->diffTable($table, $onlineTable); self::assertFalse($diff, "Tables should be identical with double quoted literals."); - } - public function testColumnDefaultCurrentTimestamp(): void { $platform = $this->_sm->getDatabasePlatform(); @@ -423,43 +390,6 @@ public function testColumnDefaultCurrentTimestamp(): void } /** - * Test default CURRENT_TIME and CURRENT_DATE as default values - * - * Note: MySQL (as of 5.7.19) does not support default value - * for DATE and TIME fields while MariaDB 10.2+ does - */ - public function testColumnDefaultCurrentTimeAndDate() - { - $platform = $this->_sm->getDatabasePlatform(); - - if (!$platform instanceof MariaDb1027Platform) { - $this->markTestSkipped('Currently only MariaDb1027Platform supports setting CURRENT_TIME and CURRENT_DATE default values.'); - } - - $table = new Table("test_column_defaults_current_time_and_date"); - - $currentTimeSql = $platform->getCurrentTimeSQL(); - $currentDateSql = $platform->getCurrentDateSQL(); - - $table->addColumn('col_date', 'date', ['notnull' => true, 'default' => $currentDateSql]); - $table->addColumn('col_time', 'time', ['notnull' => true, 'default' => $currentTimeSql]); - - $this->_sm->dropAndCreateTable($table); - - $onlineTable = $this->_sm->listTableDetails("test_column_defaults_current_time_and_date"); - - 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 defaults."); - } - - - /** - * * @link https://mariadb.com/kb/en/library/string-literals */ public function testColumnDefaultValuesEscaping(): void @@ -483,9 +413,26 @@ public function testColumnDefaultValuesEscaping(): void self::assertFalse($diff, "Tables should be identical with values escape sequences."); } - public function testColumnDefaultsUsingDoctrineTable(): void + public function testJsonColumnType(): void { + $platform = $this->_sm->getDatabasePlatform(); + if (!$platform->hasNativeJsonType()) { + $this->markTestSkipped("Requires native JSON type"); + } + + $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()); + } + /** + * @todo split into multiple tests (most of them already made) and remove + */ + public function testColumnDefaultsUsingDoctrineTable(): void + { $table = new Table("test_column_defaults_with_table"); $table->addColumn('col0', 'integer', ['notnull' => false]); $table->addColumn('col1', 'integer', ['notnull' => false, 'default' => null]); @@ -534,20 +481,22 @@ public function testColumnDefaultsUsingDoctrineTable(): void } /** - * Ensure that an existing table with quoted (literals) default values - * does not trigger a table change. + * Ensure that a table created outside doctrine and containing + * quoted default values does not trigger a table diff change. I + * Note: MariaDb 10.2 silently change "\'" into "''" when storing in + * information schema, MariaDb102Platform should normalize the table details. */ - public function testColumnDefaultsDoesNotTriggerADiff(): void + public function testExistingTableWithQuotedDefaultsDoesNotTriggerChange(): void { - $this->_conn->query('DROP TABLE IF EXISTS test_column_defaults_no_diff'); + $this->_conn->query('DROP TABLE IF EXISTS test_column_defaults_with_create'); $sql = " CREATE TABLE test_column_defaults_with_create ( col1 VARCHAR(255) NULL DEFAULT 'O''Connor\'\"', col2 VARCHAR(255) NULL DEFAULT '''A''' ); "; - $this->_conn->query($sql); + $onlineTable = $this->_sm->listTableDetails("test_column_defaults_with_create"); self::assertSame("O'Connor'\"", $onlineTable->getColumn('col1')->getDefault()); self::assertSame("'A'", $onlineTable->getColumn('col2')->getDefault()); @@ -561,31 +510,101 @@ public function testColumnDefaultsDoesNotTriggerADiff(): void self::assertFalse($diff); } + /** + * Since MariaDB 10.2.1, Blob and text columns can have a default value + * + * @link https://mariadb.com/kb/en/library/blob-and-text-data-types + */ + public function testDoesPropagateDefaultValuesForBlobTextAndJson(): void + { + if (!$this->_sm->getDatabasePlatform() instanceof MariaDb102Platform) { + $this->markTestSkipped('Only relevant for MariaDb102Platform.'); + } + + $table = new Table("text_blob_default_value"); + + $json = json_encode(['prop1' => "O'Connor", 'prop2' => 10]); + + $table->addColumn('def_text', 'text', ['default' => "O'Connor"]); + $table->addColumn('def_text_null', 'text', ['notnull' => false, 'default' => 'def']); + $table->addColumn('def_blob', 'blob', ['default' => 'def']); + $table->addColumn('def_json', 'json', ['default' => $json]); + + $this->_sm->dropAndCreateTable($table); + + $onlineTable = $this->_sm->listTableDetails("text_blob_default_value"); + + self::assertSame("O'Connor", $onlineTable->getColumn('def_text')->getDefault()); + self::assertSame('def', $onlineTable->getColumn('def_text_null')->getDefault()); + self::assertSame('def', $onlineTable->getColumn('def_blob')->getDefault()); + self::assertSame($json, $onlineTable->getColumn('def_json')->getDefault()); + + $comparator = new Comparator(); + + self::assertFalse($comparator->diffTable($table, $onlineTable)); + } + + /** + * Note: MySQL (as of 5.7.19) does not support default value + * for DATE and TIME fields while MariaDB 10.2+ does + */ + public function testColumnDefaultValuesCurrentTimeAndDate(): void + { + if (!$this->_sm->getDatabasePlatform() instanceof MariaDb102Platform) { + $this->markTestSkipped('Only relevant for MariaDb102Platform.'); + } + + $platform = $this->_sm->getDatabasePlatform(); + + $table = new Table("test_column_defaults_current_time_and_date"); + + $currentTimeSql = $platform->getCurrentTimeSQL(); + $currentDateSql = $platform->getCurrentDateSQL(); + + $table->addColumn('col_date', 'date', ['notnull' => true, 'default' => $currentDateSql]); + $table->addColumn('col_time', 'time', ['notnull' => true, 'default' => $currentTimeSql]); + + $this->_sm->dropAndCreateTable($table); + + $onlineTable = $this->_sm->listTableDetails("test_column_defaults_current_time_and_date"); + + 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 defaults."); + } + /** * MariaDB supports expressions as default values * + * @todo remove or implement !!! + * * @link https://mariadb.com/kb/en/library/information-schema-columns-table/ */ public function testColumnDefaultExpressions(): void { $this->markTestSkipped('Setting an expression as a default value is not yet supported (WIP)'); - if ($this->_sm->getDatabasePlatform() instanceof MariaDb1027Platform) { + if (!$this->_sm->getDatabasePlatform() instanceof MariaDb102Platform) { + $this->markTestSkipped('Only relevant for MariaDb102Platform.'); + } - $table = new Table("test_column_default_expressions"); + $table = new Table("test_column_default_expressions"); - $table->addColumn('expression', 'string', ['notnull' => false, 'default' => "concat('A','B')"]); + $table->addColumn('expression', 'string', ['notnull' => false, 'default' => "concat('A','B')"]); - $this->_sm->dropAndCreateTable($table); + $this->_sm->dropAndCreateTable($table); - $onlineTable = $this->_sm->listTableDetails("test_column_default_expressions"); - self::assertSame("concat('A','B')", $onlineTable->getColumn('expression')->getDefault()); + $onlineTable = $this->_sm->listTableDetails("test_column_default_expressions"); + self::assertSame("concat('A','B')", $onlineTable->getColumn('expression')->getDefault()); - $comparator = new Comparator(); + $comparator = new Comparator(); - $diff = $comparator->diffTable($table, $onlineTable); - self::assertFalse($diff, "Tables should be identical with expression column defaults."); - } + $diff = $comparator->diffTable($table, $onlineTable); + self::assertFalse($diff, "Tables should be identical with expression column defaults."); } } diff --git a/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php deleted file mode 100644 index 39a661bd33c..00000000000 --- a/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php +++ /dev/null @@ -1,62 +0,0 @@ -_platform->hasNativeJsonType()); - } - - public function testReturnsJsonTypeDeclarationSQL() - { - self::assertSame('JSON', $this->_platform->getJsonTypeDeclarationSQL([])); - } - - public function testInitializesJsonTypeMapping() - { - self::assertTrue($this->_platform->hasDoctrineTypeMappingFor('json')); - self::assertSame(Type::JSON, $this->_platform->getDoctrineTypeMapping('json')); - } - - /** - * Overrides AbstractMySQLPlatformTestCase::testDoesNotPropagateDefaultValuesForUnsupportedColumnTypes() - */ - public function testDoesNotPropagateDefaultValuesForUnsupportedColumnTypes() - { - $table = new Table("text_blob_default_value"); - - $table->addColumn('def_text', 'text', array('default' => 'def')); - $table->addColumn('def_blob', 'blob', array('default' => 'def')); - - self::assertSame( - array('CREATE TABLE text_blob_default_value (def_text LONGTEXT DEFAULT \'def\' NOT NULL, def_blob LONGBLOB DEFAULT \'def\' NOT NULL) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB'), - $this->_platform->getCreateTableSQL($table) - ); - - $diffTable = clone $table; - - $diffTable->changeColumn('def_text', array('default' => 'def')); - $diffTable->changeColumn('def_blob', array('default' => 'def')); - - $comparator = new Comparator(); - - self::assertFalse($comparator->diffTable($table, $diffTable)); - } - - -} diff --git a/tests/Doctrine/Tests/DBAL/Platforms/MariaDb102PlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/MariaDb102PlatformTest.php new file mode 100644 index 00000000000..9b50fc5717c --- /dev/null +++ b/tests/Doctrine/Tests/DBAL/Platforms/MariaDb102PlatformTest.php @@ -0,0 +1,94 @@ +_platform->hasNativeJsonType()); + } + + public function testReturnsJsonTypeDeclarationSQL() + { + self::assertSame('JSON', $this->_platform->getJsonTypeDeclarationSQL([])); + } + + public function testInitializesJsonTypeMapping() + { + 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() + { + $this->markTestSkipped('MariaDB102Platform support propagation of default values for BLOB and TEXT columns'); + } + + /** + * Since MariaDB 10.2, Text and Blob can have a default value. + */ + public function testPropagateDefaultValuesForTextAndBlobColumnTypes() + { + $table = new Table("text_blob_default_value"); + + $table->addColumn('def_text', 'text', array('default' => "d''ef")); + $table->addColumn('def_blob', 'blob', array('default' => 'def')); + + self::assertSame( + ["CREATE TABLE text_blob_default_value (def_text LONGTEXT DEFAULT 'd''''ef' NOT NULL, def_blob LONGBLOB DEFAULT 'def' NOT NULL) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB"], + $this->_platform->getCreateTableSQL($table) + ); + + $diffTable = clone $table; + + $diffTable->changeColumn('def_text', ['default' => "d''ef"]); + $diffTable->changeColumn('def_blob', ['default' => 'def']); + + $comparator = new Comparator(); + + self::assertFalse($comparator->diffTable($table, $diffTable)); + } + + public function testPropagateDefaultValuesForJsonColumnType() + { + $table = new Table("text_json_default_value"); + + $json = json_encode(['prop1' => "O'Connor", 'prop2' => 10]); + + $table->addColumn('def_json', 'text', ['default' => $json]); + + self::assertSame( + ["CREATE TABLE text_json_default_value (def_json LONGTEXT DEFAULT '{\"prop1\":\"O''Connor\",\"prop2\":10}' NOT NULL) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB"], + $this->_platform->getCreateTableSQL($table) + ); + + $diffTable = clone $table; + + $diffTable->changeColumn('def_json', ['default' => $json]); + + $comparator = new Comparator(); + + self::assertFalse($comparator->diffTable($table, $diffTable)); + } + +} From f5ca4587a33d91b7f7198e171bf26dff9ae00a8f Mon Sep 17 00:00:00 2001 From: belgattitude Date: Wed, 27 Sep 2017 03:18:02 +0200 Subject: [PATCH 05/45] Removed debug comment --- lib/Doctrine/DBAL/Platforms/MySqlPlatform.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php index 347b2cc2264..769b3b2d1be 100644 --- a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php @@ -596,7 +596,6 @@ public function getAlterTableSQL(TableDiff $diff) if ($columnDiff->hasChanged('default') && count($columnDiff->changedProperties) === 1 && !$this->isDefaultValueSupportedForType($columnArray['type']) - //($columnArray['type'] instanceof TextType || $columnArray['type'] instanceof BlobType) ) { continue; } From d2ddd2f0d3c8225c92809dcbc63d7aee79d80832 Mon Sep 17 00:00:00 2001 From: belgattitude Date: Wed, 27 Sep 2017 03:41:15 +0200 Subject: [PATCH 06/45] Cleanup --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b480e9cf066..ca5892be3d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -119,7 +119,6 @@ jobs: addons: mariadb: 10.2 - - stage: Test php: 7.1 env: DB=mariadb.mysqli MARIADB_VERSION=10.2 @@ -136,7 +135,6 @@ jobs: addons: mariadb: 10.2 - - stage: Test php: 7.1 env: DB=pgsql POSTGRESQL_VERSION=9.2 From 0a34a67c59ad4caeb74f66f837362c6bfc38495a Mon Sep 17 00:00:00 2001 From: belgattitude Date: Wed, 27 Sep 2017 14:03:32 +0200 Subject: [PATCH 07/45] More tests for escaping --- .../Schema/MySqlSchemaManagerTest.php | 18 ++++++++++++------ .../AbstractMySQLPlatformTestCase.php | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index 1039f4b25bc..44371fdfeef 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -390,22 +390,28 @@ public function testColumnDefaultCurrentTimestamp(): void } /** + * Test that default value escaping does not trigger a schema change + * using different escaping options. * @link https://mariadb.com/kb/en/library/string-literals + * @link https://dev.mysql.com/doc/refman/5.7/en/string-literals.html */ public function testColumnDefaultValuesEscaping(): void { $table = new Table("test_column_default_values_escaping"); - $table->addColumn('no_quotes', 'string', ['notnull' => false, 'default' => 'az']); - - $table->addColumn('backslash', 'string', ['notnull' => false, 'default' => 'a\\\z']); - $table->addColumn('repeated_single_quotes', 'string', ['notnull' => false, 'default' => "a''z"]); + $table->addColumn('double_backslash', 'string', ['default' => 'F\\Q\\D\\N']); + $table->addColumn('triple_backslash', 'string', ['default' => 'a\\\z']); + $table->addColumn('repeated_single_quotes', 'string', ['default' => "a''z"]); + $table->addColumn('backslash_double_quote', 'string', ['default' => 'a\"z']); + $table->addColumn('backslash_newline', 'string', ['default' => 'a\nz']); $this->_sm->dropAndCreateTable($table); $onlineTable = $this->_sm->listTableDetails("test_column_default_values_escaping"); - self::assertSame("az", $onlineTable->getColumn('no_quotes')->getDefault()); - self::assertSame('a\\\z', $onlineTable->getColumn('backslash')->getDefault()); + self::assertSame('F\\Q\\D\\N', $onlineTable->getColumn('double_backslash')->getDefault()); + self::assertSame('a\\\z', $onlineTable->getColumn('triple_backslash')->getDefault()); self::assertSame("a''z", $onlineTable->getColumn('repeated_single_quotes')->getDefault()); + self::assertSame('a\"z', $onlineTable->getColumn('backslash_double_quote')->getDefault()); + self::assertSame('a\nz', $onlineTable->getColumn('backslash_newline')->getDefault()); $comparator = new Comparator(); diff --git a/tests/Doctrine/Tests/DBAL/Platforms/AbstractMySQLPlatformTestCase.php b/tests/Doctrine/Tests/DBAL/Platforms/AbstractMySQLPlatformTestCase.php index 19d7407a8c6..86d2c6c2a2e 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/AbstractMySQLPlatformTestCase.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/AbstractMySQLPlatformTestCase.php @@ -917,7 +917,7 @@ public function testGetDefaultValueDeclarationSQLIsQuotedWithLiteral() ]; self::assertSame(sprintf( - " DEFAULT %s", + ' DEFAULT %s', $this->_platform->quoteStringLiteral("'O'Connor said: \"Hello\" \ \r'") ), $this->_platform->getDefaultValueDeclarationSQL($field) From f6cb85322c22ed58c187adac9c89c11605222f42 Mon Sep 17 00:00:00 2001 From: belgattitude Date: Wed, 27 Sep 2017 15:40:18 +0200 Subject: [PATCH 08/45] Updated tests --- .../Schema/MySqlSchemaManagerTest.php | 176 ++++++------------ 1 file changed, 62 insertions(+), 114 deletions(-) diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index 44371fdfeef..2d88a50701a 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -333,7 +333,6 @@ public function testListFloatTypeColumns() self::assertTrue($columns['col_unsigned']->getUnsigned()); } - /** * As of MariaDB 10.2.7, nullable default values literals are always single quoted in * information_schema. Non-nullable defaults behaviour is not affected. @@ -366,38 +365,14 @@ public function testColumnDefaultValuesDoubleQuoted(): void self::assertFalse($diff, "Tables should be identical with double quoted literals."); } - 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', ['notnull' => false, '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."); - } - /** - * Test that default value escaping does not trigger a schema change - * using different escaping options. * @link https://mariadb.com/kb/en/library/string-literals * @link https://dev.mysql.com/doc/refman/5.7/en/string-literals.html */ - public function testColumnDefaultValuesEscaping(): void + public function testColumnEscapingDefaultValuesDoesNotTriggerSchemaChange(): void { $table = new Table("test_column_default_values_escaping"); + $table->addColumn('single_backslash', 'string', ['default' => 'F\Q\D\N']); $table->addColumn('double_backslash', 'string', ['default' => 'F\\Q\\D\\N']); $table->addColumn('triple_backslash', 'string', ['default' => 'a\\\z']); $table->addColumn('repeated_single_quotes', 'string', ['default' => "a''z"]); @@ -407,6 +382,7 @@ public function testColumnDefaultValuesEscaping(): void $this->_sm->dropAndCreateTable($table); $onlineTable = $this->_sm->listTableDetails("test_column_default_values_escaping"); + self::assertSame('F\Q\D\N', $onlineTable->getColumn('single_backslash')->getDefault()); self::assertSame('F\\Q\\D\\N', $onlineTable->getColumn('double_backslash')->getDefault()); self::assertSame('a\\\z', $onlineTable->getColumn('triple_backslash')->getDefault()); self::assertSame("a''z", $onlineTable->getColumn('repeated_single_quotes')->getDefault()); @@ -434,57 +410,6 @@ public function testJsonColumnType(): void self::assertSame(TYPE::JSON, $columns['col_json']->getType()->getName()); } - /** - * @todo split into multiple tests (most of them already made) and remove - */ - public function testColumnDefaultsUsingDoctrineTable(): void - { - $table = new Table("test_column_defaults_with_table"); - $table->addColumn('col0', 'integer', ['notnull' => false]); - $table->addColumn('col1', 'integer', ['notnull' => false, 'default' => null]); - $table->addColumn('col2', 'string', ['notnull' => false, 'default' => null]); - $table->addColumn('col3', 'string', ['notnull' => false, 'default' => 'NULL']); - $table->addColumn('col4', 'string', ['notnull' => false, 'default' => 'Hello world']); - $table->addColumn('col5', 'datetime', ['notnull' => false, 'default' => null]); - $table->addColumn('col6', 'decimal', ['scale' => 3, 'precision' => 6, 'notnull' => false, 'default' => -2.3]); - $table->addColumn('col7', 'date', ['notnull' => false, 'default' => '2012-12-12']); - $table->addColumn('col8', 'string', ['notnull' => true, 'default' => '']); - $table->addColumn('col9', 'integer', ['notnull' => false, 'default' => 0]); - $table->addColumn('col10', 'string', ['notnull' => false, 'default' => 'He"ll"o world']); - $table->addColumn('col11', 'string', ['notnull' => false, 'default' => '2012-12-12 23:59:59']); - //$table->addColumn('col12', 'string', ['notnull' => false, 'default' => 'He\\\'llo \\\world']); - - $table->addColumn('col20', 'string', ['notnull' => true, 'default' => 'CURRENT_TIMESTAMP()']); - $table->addColumn('col21', 'string', ['notnull' => false, 'default' => 'CURRENT_TIMESTAMP']); - - $this->_sm->dropAndCreateTable($table); - - $onlineTable = $this->_sm->listTableDetails("test_column_defaults_with_table"); - self::assertNull($onlineTable->getColumn('col0')->getDefault()); - self::assertNull($onlineTable->getColumn('col1')->getDefault()); - self::assertNull($onlineTable->getColumn('col2')->getDefault()); - self::assertEquals('NULL', $onlineTable->getColumn('col3')->getDefault()); - self::assertEquals('Hello world', $onlineTable->getColumn('col4')->getDefault()); - self::assertNull($onlineTable->getColumn('col5')->getDefault()); - self::assertEquals(-2.3, $onlineTable->getColumn('col6')->getDefault()); - self::assertEquals('2012-12-12', $onlineTable->getColumn('col7')->getDefault()); - self::assertTrue($onlineTable->getColumn('col8')->getNotnull()); - self::assertEquals('', $onlineTable->getColumn('col8')->getDefault()); - self::assertSame('0', $onlineTable->getColumn('col9')->getDefault()); - self::assertEquals('He"ll"o world', $onlineTable->getColumn('col10')->getDefault()); - self::assertEquals('2012-12-12 23:59:59', $onlineTable->getColumn('col11')->getDefault()); - //self::assertEquals('He\\\'llo \\world', $onlineTable->getColumn('col12')->getDefault()); - - // MariaDB 10.2 and MySQL 5.7 differences while storing default now() in information schema. - // MariaDB will always store "current_timestamp()", mysql "CURRENT_TIMESTAMP" - self::assertStringStartsWith('current_timestamp', strtolower($onlineTable->getColumn('col20')->getDefault())); - self::assertStringStartsWith('current_timestamp', strtolower($onlineTable->getColumn('col21')->getDefault())); - - $comparator = new Comparator(); - - $diff = $comparator->diffTable($table, $onlineTable); - self::assertFalse($diff, "Tables should be identical with column defaults."); - } /** * Ensure that a table created outside doctrine and containing @@ -516,38 +441,58 @@ public function testExistingTableWithQuotedDefaultsDoesNotTriggerChange(): void self::assertFalse($diff); } - /** - * Since MariaDB 10.2.1, Blob and text columns can have a default value - * - * @link https://mariadb.com/kb/en/library/blob-and-text-data-types - */ - public function testDoesPropagateDefaultValuesForBlobTextAndJson(): void + public function testColumnDefaultCurrentTimestamp(): void { - if (!$this->_sm->getDatabasePlatform() instanceof MariaDb102Platform) { - $this->markTestSkipped('Only relevant for MariaDb102Platform.'); - } + $platform = $this->_sm->getDatabasePlatform(); - $table = new Table("text_blob_default_value"); + $table = new Table("test_column_defaults_current_timestamp"); - $json = json_encode(['prop1' => "O'Connor", 'prop2' => 10]); + $currentTimeStampSql = $platform->getCurrentTimestampSQL(); - $table->addColumn('def_text', 'text', ['default' => "O'Connor"]); - $table->addColumn('def_text_null', 'text', ['notnull' => false, 'default' => 'def']); - $table->addColumn('def_blob', 'blob', ['default' => 'def']); - $table->addColumn('def_json', 'json', ['default' => $json]); + $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("text_blob_default_value"); - - self::assertSame("O'Connor", $onlineTable->getColumn('def_text')->getDefault()); - self::assertSame('def', $onlineTable->getColumn('def_text_null')->getDefault()); - self::assertSame('def', $onlineTable->getColumn('def_blob')->getDefault()); - self::assertSame($json, $onlineTable->getColumn('def_json')->getDefault()); + $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(); - self::assertFalse($comparator->diffTable($table, $onlineTable)); + $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_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('1', $row['col_int']); + self::assertSame('A', $row['col_string']); + self::assertSame('-2.300', $row['col_decimal']); + self::assertSame('2012-12-12', $row['col_date']); + self::assertLessThan(5, $row['diff_seconds']); } /** @@ -584,33 +529,36 @@ public function testColumnDefaultValuesCurrentTimeAndDate(): void } /** - * MariaDB supports expressions as default values - * - * @todo remove or implement !!! + * Since MariaDB 10.2.1, Blob and text columns can have a default value * - * @link https://mariadb.com/kb/en/library/information-schema-columns-table/ + * @link https://mariadb.com/kb/en/library/blob-and-text-data-types */ - public function testColumnDefaultExpressions(): void + public function testDoesPropagateDefaultValuesForBlobTextAndJson(): void { - $this->markTestSkipped('Setting an expression as a default value is not yet supported (WIP)'); - if (!$this->_sm->getDatabasePlatform() instanceof MariaDb102Platform) { $this->markTestSkipped('Only relevant for MariaDb102Platform.'); } - $table = new Table("test_column_default_expressions"); + $table = new Table("text_blob_default_value"); + + $json = json_encode(['prop1' => "O'Connor", 'prop2' => 10]); - $table->addColumn('expression', 'string', ['notnull' => false, 'default' => "concat('A','B')"]); + $table->addColumn('def_text', 'text', ['default' => "O'Connor"]); + $table->addColumn('def_text_null', 'text', ['notnull' => false, 'default' => 'def']); + $table->addColumn('def_blob', 'blob', ['default' => '\F\Q\D\N']); + $table->addColumn('def_json', 'json', ['default' => $json]); $this->_sm->dropAndCreateTable($table); - $onlineTable = $this->_sm->listTableDetails("test_column_default_expressions"); - self::assertSame("concat('A','B')", $onlineTable->getColumn('expression')->getDefault()); + $onlineTable = $this->_sm->listTableDetails("text_blob_default_value"); + + self::assertSame("O'Connor", $onlineTable->getColumn('def_text')->getDefault()); + self::assertSame('def', $onlineTable->getColumn('def_text_null')->getDefault()); + self::assertSame('\F\Q\D\N', $onlineTable->getColumn('def_blob')->getDefault()); + self::assertSame($json, $onlineTable->getColumn('def_json')->getDefault()); $comparator = new Comparator(); - $diff = $comparator->diffTable($table, $onlineTable); - self::assertFalse($diff, "Tables should be identical with expression column defaults."); + self::assertFalse($comparator->diffTable($table, $onlineTable)); } - } From 0d20c853d9457d3432b2a0e4ad8ba2cf31c1c211 Mon Sep 17 00:00:00 2001 From: belgattitude Date: Wed, 27 Sep 2017 15:57:53 +0200 Subject: [PATCH 09/45] Added more tests for CURRENT_* defaults --- .../Schema/MySqlSchemaManagerTest.php | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index 2d88a50701a..c7f1516a7e9 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -496,8 +496,14 @@ public function testColumnDefaultsAreValid() { } /** - * Note: MySQL (as of 5.7.19) does not support default value - * for DATE and TIME fields while MariaDB 10.2+ does + * 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 { @@ -509,23 +515,26 @@ public function testColumnDefaultValuesCurrentTimeAndDate(): void $table = new Table("test_column_defaults_current_time_and_date"); + $currentTimestampSql = $platform->getCurrentTimestampSQL(); $currentTimeSql = $platform->getCurrentTimeSQL(); $currentDateSql = $platform->getCurrentDateSQL(); - $table->addColumn('col_date', 'date', ['notnull' => true, 'default' => $currentDateSql]); - $table->addColumn('col_time', 'time', ['notnull' => true, 'default' => $currentTimeSql]); + $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 defaults."); + self::assertFalse($diff, "Tables should be identical with column defauts time and date."); } /** From a4d3068fd95d84aca8e6da19a3981a98df8f4cea Mon Sep 17 00:00:00 2001 From: belgattitude Date: Wed, 27 Sep 2017 16:12:52 +0200 Subject: [PATCH 10/45] Minor cleanup --- lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index 825f3d96daf..1f2896988f8 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -177,10 +177,6 @@ protected function _getPortableTableColumnDefinition($tableColumn) break; } - $length = $length !== null ? (int) $length : null; - - $isNotNull = $tableColumn['null'] !== 'YES'; - if ($this->_platform instanceof MariaDb102Platform) { $columnDefault = $this->getMariaDb1027ColumnDefault($this->_platform, $tableColumn['default'] ?? null); } else { @@ -188,11 +184,11 @@ protected function _getPortableTableColumnDefinition($tableColumn) } $options = [ - 'length' => $length, + 'length' => $length !== null ? (int) $length : null, 'unsigned' => strpos($tableColumn['type'], 'unsigned') !== false, 'fixed' => (bool) $fixed, 'default' => $columnDefault, - 'notnull' => $isNotNull, + 'notnull' => $tableColumn['null'] !== 'YES', 'scale' => null, 'precision' => null, 'autoincrement' => strpos($tableColumn['extra'], 'auto_increment') !== false, From c86b622969a0a794ed29a50c7e1e704e8b08ae4b Mon Sep 17 00:00:00 2001 From: belgattitude Date: Wed, 11 Oct 2017 10:42:14 +0200 Subject: [PATCH 11/45] Fixed typings --- lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php b/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php index a78306a764f..3fb7cca1818 100644 --- a/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php +++ b/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php @@ -21,6 +21,7 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\DrizzlePlatform; +use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\DrizzleSchemaManager; /** @@ -56,7 +57,7 @@ public function createDatabasePlatformForVersion($version): AbstractPlatform /** * {@inheritdoc} */ - public function getDatabasePlatform() + public function getDatabasePlatform(): AbstractPlatform { return new DrizzlePlatform(); } @@ -64,7 +65,7 @@ public function getDatabasePlatform() /** * {@inheritdoc} */ - public function getSchemaManager(\Doctrine\DBAL\Connection $conn) + public function getSchemaManager(\Doctrine\DBAL\Connection $conn): AbstractSchemaManager { return new DrizzleSchemaManager($conn); } From 61bed75db85759385215a68ce145673c4a6dddfd Mon Sep 17 00:00:00 2001 From: belgattitude Date: Wed, 11 Oct 2017 11:41:11 +0200 Subject: [PATCH 12/45] Fixed strict assert for default int values --- lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php | 4 ++-- .../DBAL/Functional/Schema/MySqlSchemaManagerTest.php | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index 1f2896988f8..b1b93fdfdd0 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -198,8 +198,8 @@ protected function _getPortableTableColumnDefinition($tableColumn) ]; 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); diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index c7f1516a7e9..a19b74b5960 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -472,6 +472,7 @@ public function testColumnDefaultsAreValid() { $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']); @@ -488,10 +489,11 @@ public function testColumnDefaultsAreValid() { self::assertInstanceOf(\DateTime::class, \DateTime::createFromFormat('Y-m-d H:i:s', $row['col_datetime'])); self::assertNull($row['col_datetime_null']); - self::assertSame('1', $row['col_int']); - self::assertSame('A', $row['col_string']); - self::assertSame('-2.300', $row['col_decimal']); 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']); } From 2dda57a9f257ac573dcb731bae65a1c392757133 Mon Sep 17 00:00:00 2001 From: belgattitude Date: Wed, 11 Oct 2017 12:16:43 +0200 Subject: [PATCH 13/45] Added final tests for default escaping --- .../AbstractPostgreSqlPlatformTestCase.php | 15 +++++++++++++++ .../DBAL/Platforms/MariaDb102PlatformTest.php | 1 - .../Tests/DBAL/Platforms/OraclePlatformTest.php | 15 +++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/tests/Doctrine/Tests/DBAL/Platforms/AbstractPostgreSqlPlatformTestCase.php b/tests/Doctrine/Tests/DBAL/Platforms/AbstractPostgreSqlPlatformTestCase.php index c3809f438be..50e38a15895 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/AbstractPostgreSqlPlatformTestCase.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/AbstractPostgreSqlPlatformTestCase.php @@ -976,4 +976,19 @@ public function testQuotesDatabaseNameInCloseActiveDatabaseConnectionsSQL() true ); } + + public function testGetDefaultValueDeclarationSQLIsQuotedWithLiteral() + { + $field = [ + 'type' => Type::getType('string'), + 'default' => "'O'Connor said: \"Hello\" \ \r'" + ]; + + self::assertSame(sprintf( + ' DEFAULT %s', + $this->_platform->quoteStringLiteral("'O'Connor said: \"Hello\" \ \r'") + ), + $this->_platform->getDefaultValueDeclarationSQL($field) + ); + } } diff --git a/tests/Doctrine/Tests/DBAL/Platforms/MariaDb102PlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/MariaDb102PlatformTest.php index 9b50fc5717c..54636119722 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/MariaDb102PlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/MariaDb102PlatformTest.php @@ -90,5 +90,4 @@ public function testPropagateDefaultValuesForJsonColumnType() self::assertFalse($comparator->diffTable($table, $diffTable)); } - } diff --git a/tests/Doctrine/Tests/DBAL/Platforms/OraclePlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/OraclePlatformTest.php index 7ebbea2d11f..1c0c1c735bd 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/OraclePlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/OraclePlatformTest.php @@ -874,4 +874,19 @@ public function testQuotesDatabaseNameInListTableColumnsSQL() { self::assertContains("'Foo''Bar\\\\'", $this->_platform->getListTableColumnsSQL('foo_table', "Foo'Bar\\"), '', true); } + + public function testGetDefaultValueDeclarationSQLIsQuotedWithLiteral() + { + $field = [ + 'type' => Type::getType('string'), + 'default' => "'O'Connor said: \"Hello\" \ \r'" + ]; + + self::assertSame(sprintf( + ' DEFAULT %s', + $this->_platform->quoteStringLiteral("'O'Connor said: \"Hello\" \ \r'") + ), + $this->_platform->getDefaultValueDeclarationSQL($field) + ); + } } From d4c2be87e52d024e5c47556d5c1e8c09a7962de2 Mon Sep 17 00:00:00 2001 From: belgattitude Date: Wed, 11 Oct 2017 13:08:48 +0200 Subject: [PATCH 14/45] csfix: There must be a single space before the colon on return types --- lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php | 12 ++++++------ .../DBAL/Driver/DrizzlePDOMySql/Driver.php | 6 +++--- .../DBAL/Platforms/Keywords/MariaDb102Keywords.php | 4 ++-- lib/Doctrine/DBAL/Platforms/MariaDb102Platform.php | 10 +++++----- lib/Doctrine/DBAL/Platforms/MySqlPlatform.php | 2 +- lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php | 2 +- .../Functional/Schema/MySqlSchemaManagerTest.php | 14 +++++++------- 7 files changed, 25 insertions(+), 25 deletions(-) diff --git a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php index e8f86bed69b..78af1be2e71 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php @@ -130,7 +130,7 @@ public function convertException($message, DriverException $exception) * @return AbstractPlatform|MariaDb102Platform|MySQL57Platform|MySqlPlatform * @throws DBALException */ - public function createDatabasePlatformForVersion($version): AbstractPlatform + public function createDatabasePlatformForVersion($version) : AbstractPlatform { if (false !== stripos($version, 'mariadb')) { $versionNumber = $this->getMariaDbMysqlVersionNumber($version); @@ -154,7 +154,7 @@ public function createDatabasePlatformForVersion($version): AbstractPlatform * @param string $versionString Version string returned by the driver, i.e. '5.7.10' * @throws DBALException */ - private function getOracleMysqlVersionNumber(string $versionString): string + private function getOracleMysqlVersionNumber(string $versionString) : string { if (!preg_match('/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $versionString, $versionParts)) { throw DBALException::invalidPlatformVersionSpecified( @@ -180,7 +180,7 @@ private function getOracleMysqlVersionNumber(string $versionString): string * @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 + private function getMariaDbMysqlVersionNumber(string $versionString) : string { $version = str_replace('5.5.5-', '', $versionString); @@ -197,7 +197,7 @@ private function getMariaDbMysqlVersionNumber(string $versionString): string /** * {@inheritdoc} */ - public function getDatabase(\Doctrine\DBAL\Connection $conn): ?string + public function getDatabase(\Doctrine\DBAL\Connection $conn) : ?string { $params = $conn->getParams(); @@ -212,7 +212,7 @@ public function getDatabase(\Doctrine\DBAL\Connection $conn): ?string * {@inheritdoc} * @return MySqlPlatform */ - public function getDatabasePlatform(): AbstractPlatform + public function getDatabasePlatform() : AbstractPlatform { return new MySqlPlatform(); } @@ -221,7 +221,7 @@ public function getDatabasePlatform(): AbstractPlatform * {@inheritdoc} * @return MySqlSchemaManager */ - public function getSchemaManager(\Doctrine\DBAL\Connection $conn): AbstractSchemaManager + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) : AbstractSchemaManager { return new MySqlSchemaManager($conn); } diff --git a/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php b/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php index 3fb7cca1818..c4380592170 100644 --- a/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php +++ b/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php @@ -49,7 +49,7 @@ public function connect(array $params, $username = null, $password = null, array /** * {@inheritdoc} */ - public function createDatabasePlatformForVersion($version): AbstractPlatform + public function createDatabasePlatformForVersion($version) : AbstractPlatform { return $this->getDatabasePlatform(); } @@ -57,7 +57,7 @@ public function createDatabasePlatformForVersion($version): AbstractPlatform /** * {@inheritdoc} */ - public function getDatabasePlatform(): AbstractPlatform + public function getDatabasePlatform() : AbstractPlatform { return new DrizzlePlatform(); } @@ -65,7 +65,7 @@ public function getDatabasePlatform(): AbstractPlatform /** * {@inheritdoc} */ - public function getSchemaManager(\Doctrine\DBAL\Connection $conn): AbstractSchemaManager + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) : AbstractSchemaManager { return new DrizzleSchemaManager($conn); } diff --git a/lib/Doctrine/DBAL/Platforms/Keywords/MariaDb102Keywords.php b/lib/Doctrine/DBAL/Platforms/Keywords/MariaDb102Keywords.php index 70cbf3af38b..7563f3193f6 100644 --- a/lib/Doctrine/DBAL/Platforms/Keywords/MariaDb102Keywords.php +++ b/lib/Doctrine/DBAL/Platforms/Keywords/MariaDb102Keywords.php @@ -28,7 +28,7 @@ final class MariaDb102Keywords extends MySQLKeywords /** * {@inheritdoc} */ - public function getName(): string + public function getName() : string { return 'MariaDb102'; } @@ -36,7 +36,7 @@ public function getName(): string /** * {@inheritdoc} */ - protected function getKeywords(): array + protected function getKeywords() : array { return [ 'ACCESSIBLE', diff --git a/lib/Doctrine/DBAL/Platforms/MariaDb102Platform.php b/lib/Doctrine/DBAL/Platforms/MariaDb102Platform.php index 5688c3e9db1..60516fc404a 100644 --- a/lib/Doctrine/DBAL/Platforms/MariaDb102Platform.php +++ b/lib/Doctrine/DBAL/Platforms/MariaDb102Platform.php @@ -34,7 +34,7 @@ final class MariaDb102Platform extends MySqlPlatform /** * {@inheritdoc} */ - public function hasNativeJsonType(): bool + public function hasNativeJsonType() : bool { return true; } @@ -43,7 +43,7 @@ public function hasNativeJsonType(): bool * {@inheritdoc} * @link https://mariadb.com/kb/en/library/json-data-type/ */ - public function getJsonTypeDeclarationSQL(array $field): string + public function getJsonTypeDeclarationSQL(array $field) : string { return 'JSON'; } @@ -51,7 +51,7 @@ public function getJsonTypeDeclarationSQL(array $field): string /** * {@inheritdoc} */ - protected function getReservedKeywordsClass(): string + protected function getReservedKeywordsClass() : string { return Keywords\MariaDb102Keywords::class; } @@ -59,7 +59,7 @@ protected function getReservedKeywordsClass(): string /** * {@inheritdoc} */ - protected function initializeDoctrineTypeMappings(): void + protected function initializeDoctrineTypeMappings() : void { parent::initializeDoctrineTypeMappings(); @@ -72,7 +72,7 @@ protected function initializeDoctrineTypeMappings(): void * Since MariaDB 10.2.1 blob and text columns can have a default value. * @link https://mariadb.com/kb/en/library/blob-and-text-data-types/ */ - protected function isDefaultValueSupportedForType(Type $field): bool + protected function isDefaultValueSupportedForType(Type $field) : bool { return true; } diff --git a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php index 769b3b2d1be..43af7945b25 100644 --- a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php @@ -466,7 +466,7 @@ protected function _getCreateTableSQL($tableName, array $columns, array $options * MySQL (as of 5.7.19) does not support default values for Blob and Text * columns while MariaDB 10.2.1 does. */ - protected function isDefaultValueSupportedForType(Type $field): bool + protected function isDefaultValueSupportedForType(Type $field) : bool { return !($field instanceof TextType || $field instanceof BlobType); } diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index b1b93fdfdd0..e9b7c3ab3e1 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -226,7 +226,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) * * @param null|string $columnDefault default value as stored in information_schema for MariaDB >= 10.2.7 */ - private function getMariaDb1027ColumnDefault(MariaDb102Platform $platform, ?string $columnDefault): ?string { + private function getMariaDb1027ColumnDefault(MariaDb102Platform $platform, ?string $columnDefault) : ?string { if ($columnDefault === 'NULL' || $columnDefault === null) { $defaultValue = null; } elseif (strpos($columnDefault, "'") === 0) { diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index a19b74b5960..a522e14e678 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -341,7 +341,7 @@ public function testListFloatTypeColumns() * @link https://mariadb.com/kb/en/library/information-schema-columns-table/ * @link https://dev.mysql.com/doc/refman/5.5/en/string-literals.html */ - public function testColumnDefaultValuesDoubleQuoted(): void + public function testColumnDefaultValuesDoubleQuoted() : void { $table = new Table("test_column_default_values_double_quoted"); $table->addColumn('string_nullable_quoted', 'string', ['notnull' => false, 'default' => 'NULL']); @@ -369,7 +369,7 @@ public function testColumnDefaultValuesDoubleQuoted(): void * @link https://mariadb.com/kb/en/library/string-literals * @link https://dev.mysql.com/doc/refman/5.7/en/string-literals.html */ - public function testColumnEscapingDefaultValuesDoesNotTriggerSchemaChange(): void + public function testColumnEscapingDefaultValuesDoesNotTriggerSchemaChange() : void { $table = new Table("test_column_default_values_escaping"); $table->addColumn('single_backslash', 'string', ['default' => 'F\Q\D\N']); @@ -395,7 +395,7 @@ public function testColumnEscapingDefaultValuesDoesNotTriggerSchemaChange(): voi self::assertFalse($diff, "Tables should be identical with values escape sequences."); } - public function testJsonColumnType(): void + public function testJsonColumnType() : void { $platform = $this->_sm->getDatabasePlatform(); if (!$platform->hasNativeJsonType()) { @@ -417,7 +417,7 @@ public function testJsonColumnType(): void * Note: MariaDb 10.2 silently change "\'" into "''" when storing in * information schema, MariaDb102Platform should normalize the table details. */ - public function testExistingTableWithQuotedDefaultsDoesNotTriggerChange(): void + public function testExistingTableWithQuotedDefaultsDoesNotTriggerChange() : void { $this->_conn->query('DROP TABLE IF EXISTS test_column_defaults_with_create'); $sql = " @@ -441,7 +441,7 @@ public function testExistingTableWithQuotedDefaultsDoesNotTriggerChange(): void self::assertFalse($diff); } - public function testColumnDefaultCurrentTimestamp(): void + public function testColumnDefaultCurrentTimestamp() : void { $platform = $this->_sm->getDatabasePlatform(); @@ -507,7 +507,7 @@ public function testColumnDefaultsAreValid() { * 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 + public function testColumnDefaultValuesCurrentTimeAndDate() : void { if (!$this->_sm->getDatabasePlatform() instanceof MariaDb102Platform) { $this->markTestSkipped('Only relevant for MariaDb102Platform.'); @@ -544,7 +544,7 @@ public function testColumnDefaultValuesCurrentTimeAndDate(): void * * @link https://mariadb.com/kb/en/library/blob-and-text-data-types */ - public function testDoesPropagateDefaultValuesForBlobTextAndJson(): void + public function testDoesPropagateDefaultValuesForBlobTextAndJson() : void { if (!$this->_sm->getDatabasePlatform() instanceof MariaDb102Platform) { $this->markTestSkipped('Only relevant for MariaDb102Platform.'); From 4ddc496d951e8987a10bfbd46b72283fca30452f Mon Sep 17 00:00:00 2001 From: belgattitude Date: Wed, 11 Oct 2017 14:45:37 +0200 Subject: [PATCH 15/45] Fixes from @lcobucci review --- .../DBAL/Driver/AbstractMySQLDriver.php | 28 ++++++++++--------- .../DBAL/Schema/MySqlSchemaManager.php | 26 +++++++++-------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php index 78af1be2e71..ed4fabcda5f 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php @@ -39,6 +39,10 @@ */ abstract class AbstractMySQLDriver implements Driver, ExceptionConverterDriver, VersionAwarePlatformDriver { + + private const MYSQL_MARIADB_VERSION_REGEXP = '/^(mariadb-)?(?P\d+)\.(?P\d+)\.(?P\d+)/'; + private const MYSQL_ORACLE_VERSION_REGEXP = '/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/'; + /** * {@inheritdoc} * @@ -130,18 +134,16 @@ public function convertException($message, DriverException $exception) * @return AbstractPlatform|MariaDb102Platform|MySQL57Platform|MySqlPlatform * @throws DBALException */ - public function createDatabasePlatformForVersion($version) : AbstractPlatform + public function createDatabasePlatformForVersion($version) { - if (false !== stripos($version, 'mariadb')) { - $versionNumber = $this->getMariaDbMysqlVersionNumber($version); - if (version_compare($versionNumber, '10.2.7', '>=')) { - return new MariaDb102Platform(); - } - } else { - $versionNumber = $this->getOracleMysqlVersionNumber($version); - if (version_compare($versionNumber, '5.7.9', '>=')) { - return new MySQL57Platform(); - } + if (false !== stripos($version, 'mariadb') + && version_compare($this->getMariaDbMysqlVersionNumber($version), '10.2.7', '>=')) { + return new MariaDb102Platform(); + } + + if (false === stripos($version, 'mariadb') + && version_compare($this->getOracleMysqlVersionNumber($version), '5.7.9', '>=')) { + return new MySQL57Platform(); } return $this->getDatabasePlatform(); @@ -156,7 +158,7 @@ public function createDatabasePlatformForVersion($version) : AbstractPlatform */ private function getOracleMysqlVersionNumber(string $versionString) : string { - if (!preg_match('/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $versionString, $versionParts)) { + if (!preg_match(self::MYSQL_ORACLE_VERSION_REGEXP, $versionString, $versionParts)) { throw DBALException::invalidPlatformVersionSpecified( $versionString, '..' @@ -184,7 +186,7 @@ private function getMariaDbMysqlVersionNumber(string $versionString) : string { $version = str_replace('5.5.5-', '', $versionString); - if (!preg_match('/^(mariadb-)?(?P\d+)\.(?P\d+)\.(?P\d+)/', strtolower($version), $versionParts)) { + if (!preg_match(self::MYSQL_MARIADB_VERSION_REGEXP, strtolower($version), $versionParts)) { throw DBALException::invalidPlatformVersionSpecified( $version, '(mariadb-)?..' diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index e9b7c3ab3e1..5b8aa68733c 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -227,24 +227,26 @@ protected function _getPortableTableColumnDefinition($tableColumn) * @param null|string $columnDefault default value as stored in information_schema for MariaDB >= 10.2.7 */ private function getMariaDb1027ColumnDefault(MariaDb102Platform $platform, ?string $columnDefault) : ?string { + if ($columnDefault === 'NULL' || $columnDefault === null) { - $defaultValue = null; - } elseif (strpos($columnDefault, "'") === 0) { - $defaultValue = stripslashes( + return null; + } + if (strpos($columnDefault, "'") === 0) { + return stripslashes( str_replace("''", "'", preg_replace('/^\'(.*)\'$/', '$1', $columnDefault) ) ); - } elseif ($columnDefault === 'current_timestamp()') { - $defaultValue = $platform->getCurrentTimestampSQL(); - } elseif ($columnDefault === 'curdate()') { - $defaultValue = $platform->getCurrentDateSQL(); - } elseif ($columnDefault === 'curtime()') { - $defaultValue = $platform->getCurrentTimeSQL(); - } else { - $defaultValue = $columnDefault; } - return $defaultValue; + switch($columnDefault) { + case 'current_timestamp()': + return $platform->getCurrentTimestampSQL(); + case 'curdate()': + return $platform->getCurrentDateSQL(); + case 'curtime()': + return $platform->getCurrentTimeSQL(); + } + return $columnDefault; } /** From 6b9ead7f2cdd647ba9a5679b97ce8919e2f7e015 Mon Sep 17 00:00:00 2001 From: belgattitude Date: Thu, 12 Oct 2017 11:04:15 +0200 Subject: [PATCH 16/45] Review from @morozov about nulls --- lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php | 6 ++++++ .../DBAL/Functional/Schema/MySqlSchemaManagerTest.php | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index 5b8aa68733c..46518b5f294 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -220,9 +220,15 @@ protected function _getPortableTableColumnDefinition($tableColumn) * - CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE are stored in information_schema * as current_timestamp(), currdate(), currtime() * - Literal escaping is normalized in information schema (store "''" instead of "\'") + * - Note: columnDefault should only be null when column is not_nullable otherwise + * a string 'NULL' should be given. Unfortunately, it's not 100% correct in case of + * upgrade from previous versions: see https://jira.mariadb.org/browse/MDEV-14053. + * Otherwise we could simplify the condition === 'NULL' || === null by adding the parameter + * is_nullable. * * @link https://mariadb.com/kb/en/library/information-schema-columns-table/ * @link https://jira.mariadb.org/browse/MDEV-13132 + * @link https://jira.mariadb.org/browse/MDEV-14053 * * @param null|string $columnDefault default value as stored in information_schema for MariaDB >= 10.2.7 */ diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index a522e14e678..d5472a2dd20 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -422,19 +422,19 @@ public function testExistingTableWithQuotedDefaultsDoesNotTriggerChange() : void $this->_conn->query('DROP TABLE IF EXISTS test_column_defaults_with_create'); $sql = " CREATE TABLE test_column_defaults_with_create ( - col1 VARCHAR(255) NULL DEFAULT 'O''Connor\'\"', - col2 VARCHAR(255) NULL DEFAULT '''A''' + col1 VARCHAR(255) NULL DEFAULT 'O''Connor\'\"', + col2 VARCHAR(255) NOT NULL DEFAULT 'O''Connor\'\"' ); "; $this->_conn->query($sql); $onlineTable = $this->_sm->listTableDetails("test_column_defaults_with_create"); self::assertSame("O'Connor'\"", $onlineTable->getColumn('col1')->getDefault()); - self::assertSame("'A'", $onlineTable->getColumn('col2')->getDefault()); + self::assertSame("O'Connor'\"", $onlineTable->getColumn('col2')->getDefault()); $table = new Table("test_column_defaults_no_diff"); $table->addColumn('col1', 'string', ['notnull' => false, 'default' => "O'Connor'\""]); - $table->addColumn('col2', 'string', ['notnull' => false, 'default' => "'A'"]); + $table->addColumn('col2', 'string', ['notnull' => true, 'default' => "O'Connor'\""]); $comparator = new Comparator(); $diff = $comparator->diffTable($table, $onlineTable); From 2c2169e089fcff3a8ecb6c1a8ae191220206da4b Mon Sep 17 00:00:00 2001 From: belgattitude Date: Fri, 13 Oct 2017 09:18:47 +0200 Subject: [PATCH 17/45] @morozov review: According to MySqlPlatform::getListTableColumnsSQL(), the default column is always selected. Removed ?? --- lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index 46518b5f294..44bf637fd63 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -178,7 +178,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) } if ($this->_platform instanceof MariaDb102Platform) { - $columnDefault = $this->getMariaDb1027ColumnDefault($this->_platform, $tableColumn['default'] ?? null); + $columnDefault = $this->getMariaDb1027ColumnDefault($this->_platform, $tableColumn['default']); } else { $columnDefault = (isset($tableColumn['default'])) ? $tableColumn['default'] : null; } From e3bec40a47bc075b149f39e0de7e0351ea8e4b45 Mon Sep 17 00:00:00 2001 From: belgattitude Date: Fri, 13 Oct 2017 09:19:31 +0200 Subject: [PATCH 18/45] morozov review, revert regexp constant introduced after lcobucci review ;) --- lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php index ed4fabcda5f..452c402bef9 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php @@ -39,10 +39,6 @@ */ abstract class AbstractMySQLDriver implements Driver, ExceptionConverterDriver, VersionAwarePlatformDriver { - - private const MYSQL_MARIADB_VERSION_REGEXP = '/^(mariadb-)?(?P\d+)\.(?P\d+)\.(?P\d+)/'; - private const MYSQL_ORACLE_VERSION_REGEXP = '/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/'; - /** * {@inheritdoc} * @@ -158,7 +154,7 @@ public function createDatabasePlatformForVersion($version) */ private function getOracleMysqlVersionNumber(string $versionString) : string { - if (!preg_match(self::MYSQL_ORACLE_VERSION_REGEXP, $versionString, $versionParts)) { + if (!preg_match('/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $versionString, $versionParts)) { throw DBALException::invalidPlatformVersionSpecified( $versionString, '..' @@ -186,7 +182,7 @@ private function getMariaDbMysqlVersionNumber(string $versionString) : string { $version = str_replace('5.5.5-', '', $versionString); - if (!preg_match(self::MYSQL_MARIADB_VERSION_REGEXP, strtolower($version), $versionParts)) { + if (!preg_match('/^(mariadb-)?(?P\d+)\.(?P\d+)\.(?P\d+)/', strtolower($version), $versionParts)) { throw DBALException::invalidPlatformVersionSpecified( $version, '(mariadb-)?..' From 065386d883979445fd624ea8181202cc0f78bfce Mon Sep 17 00:00:00 2001 From: belgattitude Date: Fri, 13 Oct 2017 09:46:10 +0200 Subject: [PATCH 19/45] morozov review: revert to MariaDb1027Platform name --- lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php | 6 +++--- .../{MariaDb102Platform.php => MariaDb1027Platform.php} | 2 +- lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php | 6 +++--- .../Tests/DBAL/Driver/AbstractMySQLDriverTest.php | 8 ++++---- .../DBAL/Functional/Schema/MySqlSchemaManagerTest.php | 8 ++++---- ...aDb102PlatformTest.php => MariaDb1027PlatformTest.php} | 6 +++--- 6 files changed, 18 insertions(+), 18 deletions(-) rename lib/Doctrine/DBAL/Platforms/{MariaDb102Platform.php => MariaDb1027Platform.php} (97%) rename tests/Doctrine/Tests/DBAL/Platforms/{MariaDb102PlatformTest.php => MariaDb1027PlatformTest.php} (94%) diff --git a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php index 452c402bef9..01253c58b1b 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php @@ -23,7 +23,7 @@ use Doctrine\DBAL\Driver; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\AbstractPlatform; -use Doctrine\DBAL\Platforms\MariaDb102Platform; +use Doctrine\DBAL\Platforms\MariaDb1027Platform; use Doctrine\DBAL\Platforms\MySQL57Platform; use Doctrine\DBAL\Platforms\MySqlPlatform; use Doctrine\DBAL\Schema\AbstractSchemaManager; @@ -127,14 +127,14 @@ public function convertException($message, DriverException $exception) /** * {@inheritdoc} * - * @return AbstractPlatform|MariaDb102Platform|MySQL57Platform|MySqlPlatform + * @return AbstractPlatform|MariaDb1027Platform|MySQL57Platform|MySqlPlatform * @throws DBALException */ public function createDatabasePlatformForVersion($version) { if (false !== stripos($version, 'mariadb') && version_compare($this->getMariaDbMysqlVersionNumber($version), '10.2.7', '>=')) { - return new MariaDb102Platform(); + return new MariaDb1027Platform(); } if (false === stripos($version, 'mariadb') diff --git a/lib/Doctrine/DBAL/Platforms/MariaDb102Platform.php b/lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php similarity index 97% rename from lib/Doctrine/DBAL/Platforms/MariaDb102Platform.php rename to lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php index 60516fc404a..ad054cdb38e 100644 --- a/lib/Doctrine/DBAL/Platforms/MariaDb102Platform.php +++ b/lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php @@ -29,7 +29,7 @@ * @author Vanvelthem Sébastien * @link www.doctrine-project.org */ -final class MariaDb102Platform extends MySqlPlatform +final class MariaDb1027Platform extends MySqlPlatform { /** * {@inheritdoc} diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index 44bf637fd63..e24807417c4 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -19,7 +19,7 @@ namespace Doctrine\DBAL\Schema; -use Doctrine\DBAL\Platforms\MariaDb102Platform; +use Doctrine\DBAL\Platforms\MariaDb1027Platform; use Doctrine\DBAL\Platforms\MySqlPlatform; use Doctrine\DBAL\Types\Type; @@ -177,7 +177,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) break; } - if ($this->_platform instanceof MariaDb102Platform) { + if ($this->_platform instanceof MariaDb1027Platform) { $columnDefault = $this->getMariaDb1027ColumnDefault($this->_platform, $tableColumn['default']); } else { $columnDefault = (isset($tableColumn['default'])) ? $tableColumn['default'] : null; @@ -232,7 +232,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) * * @param null|string $columnDefault default value as stored in information_schema for MariaDB >= 10.2.7 */ - private function getMariaDb1027ColumnDefault(MariaDb102Platform $platform, ?string $columnDefault) : ?string { + private function getMariaDb1027ColumnDefault(MariaDb1027Platform $platform, ?string $columnDefault) : ?string { if ($columnDefault === 'NULL' || $columnDefault === null) { return null; diff --git a/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php b/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php index a8204f54b55..9813e06f1dd 100644 --- a/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php +++ b/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php @@ -3,7 +3,7 @@ namespace Doctrine\Tests\DBAL\Driver; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Platforms\MariaDb102Platform; +use Doctrine\DBAL\Platforms\MariaDb1027Platform; use Doctrine\DBAL\Platforms\MySQL57Platform; use Doctrine\DBAL\Platforms\MySqlPlatform; use Doctrine\DBAL\Schema\MySqlSchemaManager; @@ -69,9 +69,9 @@ protected function getDatabasePlatformsForVersions() array('10.1.2a-MariaDB-a1~lenny-log', MySqlPlatform::class), array('5.5.40-MariaDB-1~wheezy', MySqlPlatform::class), array('5.5.40-MariaDB-1~wheezy', MySqlPlatform::class), - array('5.5.5-MariaDB-10.2.8+maria~xenial-log', MariaDb102Platform::class), - array('10.2.8-MariaDB-10.2.8+maria~xenial-log', MariaDb102Platform::class), - array('10.2.8-MariaDB-1~lenny-log', MariaDb102Platform::class) + array('5.5.5-MariaDB-10.2.8+maria~xenial-log', MariaDb1027Platform::class), + array('10.2.8-MariaDB-10.2.8+maria~xenial-log', MariaDb1027Platform::class), + array('10.2.8-MariaDB-1~lenny-log', MariaDb1027Platform::class) ); } diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index d5472a2dd20..afebc082067 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -2,7 +2,7 @@ namespace Doctrine\Tests\DBAL\Functional\Schema; -use Doctrine\DBAL\Platforms\MariaDb102Platform; +use Doctrine\DBAL\Platforms\MariaDb1027Platform; use Doctrine\DBAL\Platforms\MySqlPlatform; use Doctrine\DBAL\Schema\Comparator; use Doctrine\DBAL\Schema\Schema; @@ -158,7 +158,7 @@ public function testDropPrimaryKeyWithAutoincrementColumn() */ public function testDoesNotPropagateDefaultValuesForUnsupportedColumnTypes() { - if ($this->_sm->getDatabasePlatform() instanceof MariaDb102Platform) { + if ($this->_sm->getDatabasePlatform() instanceof MariaDb1027Platform) { $this->markTestSkipped('MariaDb102Platform supports default values for BLOB and TEXT columns and will propagate values'); } @@ -509,7 +509,7 @@ public function testColumnDefaultsAreValid() { */ public function testColumnDefaultValuesCurrentTimeAndDate() : void { - if (!$this->_sm->getDatabasePlatform() instanceof MariaDb102Platform) { + if (!$this->_sm->getDatabasePlatform() instanceof MariaDb1027Platform) { $this->markTestSkipped('Only relevant for MariaDb102Platform.'); } @@ -546,7 +546,7 @@ public function testColumnDefaultValuesCurrentTimeAndDate() : void */ public function testDoesPropagateDefaultValuesForBlobTextAndJson() : void { - if (!$this->_sm->getDatabasePlatform() instanceof MariaDb102Platform) { + if (!$this->_sm->getDatabasePlatform() instanceof MariaDb1027Platform) { $this->markTestSkipped('Only relevant for MariaDb102Platform.'); } diff --git a/tests/Doctrine/Tests/DBAL/Platforms/MariaDb102PlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php similarity index 94% rename from tests/Doctrine/Tests/DBAL/Platforms/MariaDb102PlatformTest.php rename to tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php index 54636119722..94817e3de9d 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/MariaDb102PlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php @@ -2,19 +2,19 @@ namespace Doctrine\Tests\DBAL\Platforms; -use Doctrine\DBAL\Platforms\MariaDb102Platform; +use Doctrine\DBAL\Platforms\MariaDb1027Platform; use Doctrine\DBAL\Schema\Comparator; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Types\Type; -class MariaDb102PlatformTest extends AbstractMySQLPlatformTestCase +class MariaDb1027PlatformTest extends AbstractMySQLPlatformTestCase { /** * {@inheritdoc} */ public function createPlatform() { - return new MariaDb102Platform(); + return new MariaDb1027Platform(); } public function testHasNativeJsonType() From f81afb19ff98d93d7c74994ce30d70ed804d3069 Mon Sep 17 00:00:00 2001 From: belgattitude Date: Fri, 13 Oct 2017 09:49:31 +0200 Subject: [PATCH 20/45] morozov removed type doc: AbstractPlatform cover everything --- lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php index 01253c58b1b..1e86f9d0c42 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php @@ -127,7 +127,7 @@ public function convertException($message, DriverException $exception) /** * {@inheritdoc} * - * @return AbstractPlatform|MariaDb1027Platform|MySQL57Platform|MySqlPlatform + * @return AbstractPlatform * @throws DBALException */ public function createDatabasePlatformForVersion($version) From 5422bae0a5a5f201b0cad1aa4078a41ea307a827 Mon Sep 17 00:00:00 2001 From: belgattitude Date: Fri, 13 Oct 2017 10:24:31 +0200 Subject: [PATCH 21/45] Majkl578 fix bc-break with return type --- lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php index 1e86f9d0c42..eddd93320e6 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php @@ -132,13 +132,12 @@ public function convertException($message, DriverException $exception) */ public function createDatabasePlatformForVersion($version) { - if (false !== stripos($version, 'mariadb') - && version_compare($this->getMariaDbMysqlVersionNumber($version), '10.2.7', '>=')) { + $mariadb = false !== stripos($version, 'mariadb'); + if ($mariadb && version_compare($this->getMariaDbMysqlVersionNumber($version), '10.2.7', '>=')) { return new MariaDb1027Platform(); } - if (false === stripos($version, 'mariadb') - && version_compare($this->getOracleMysqlVersionNumber($version), '5.7.9', '>=')) { + if (!$mariadb && version_compare($this->getOracleMysqlVersionNumber($version), '5.7.9', '>=')) { return new MySQL57Platform(); } @@ -195,7 +194,7 @@ private function getMariaDbMysqlVersionNumber(string $versionString) : string /** * {@inheritdoc} */ - public function getDatabase(\Doctrine\DBAL\Connection $conn) : ?string + public function getDatabase(\Doctrine\DBAL\Connection $conn) { $params = $conn->getParams(); From 4747fd0c564f2dab69205f878f5f7eef17d4df9e Mon Sep 17 00:00:00 2001 From: belgattitude Date: Fri, 13 Oct 2017 12:06:22 +0200 Subject: [PATCH 22/45] Latest reviews fixes: morozov, Majkl578 --- lib/Doctrine/DBAL/Platforms/MySqlPlatform.php | 4 +-- .../DBAL/Schema/MySqlSchemaManager.php | 4 +-- .../DBAL/Driver/AbstractMySQLDriverTest.php | 36 +++++++++---------- .../Driver/DrizzlePDOMySql/DriverTest.php | 12 +++---- .../AbstractMySQLPlatformTestCase.php | 15 -------- .../AbstractPostgreSqlPlatformTestCase.php | 15 -------- .../Platforms/MariaDb1027PlatformTest.php | 14 ++++---- .../DBAL/Platforms/OraclePlatformTest.php | 15 -------- 8 files changed, 35 insertions(+), 80 deletions(-) diff --git a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php index 43af7945b25..6ce6a16f2c1 100644 --- a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php @@ -477,7 +477,7 @@ protected function isDefaultValueSupportedForType(Type $field) : bool public function getDefaultValueDeclarationSQL($field) { // Unset the default value if the given field type does not allow default values. - if (!$this->isDefaultValueSupportedForType($field['type'])) { + if (! $this->isDefaultValueSupportedForType($field['type'])) { $field['default'] = null; } @@ -595,7 +595,7 @@ public function getAlterTableSQL(TableDiff $diff) // Don't propagate default value changes for unsupported column types. if ($columnDiff->hasChanged('default') && count($columnDiff->changedProperties) === 1 && - !$this->isDefaultValueSupportedForType($columnArray['type']) + ! $this->isDefaultValueSupportedForType($columnArray['type']) ) { continue; } diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index e24807417c4..c646c404138 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -180,7 +180,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) if ($this->_platform instanceof MariaDb1027Platform) { $columnDefault = $this->getMariaDb1027ColumnDefault($this->_platform, $tableColumn['default']); } else { - $columnDefault = (isset($tableColumn['default'])) ? $tableColumn['default'] : null; + $columnDefault = $tableColumn['default']; } $options = [ @@ -237,7 +237,7 @@ private function getMariaDb1027ColumnDefault(MariaDb1027Platform $platform, ?str if ($columnDefault === 'NULL' || $columnDefault === null) { return null; } - if (strpos($columnDefault, "'") === 0) { + if ($columnDefault[0] === "'") { return stripslashes( str_replace("''", "'", preg_replace('/^\'(.*)\'$/', '$1', $columnDefault) diff --git a/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php b/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php index 9813e06f1dd..0aaf76af356 100644 --- a/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php +++ b/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php @@ -54,25 +54,25 @@ protected function createSchemaManager(Connection $connection) return new MySqlSchemaManager($connection); } - protected function getDatabasePlatformsForVersions() + protected function getDatabasePlatformsForVersions(): array { - return array( - array('5.6.9', MySqlPlatform::class), - array('5.7', MySQL57Platform::class), - array('5.7.0', MySqlPlatform::class), - array('5.7.8', MySqlPlatform::class), - array('5.7.9', MySQL57Platform::class), - array('5.7.10', MySQL57Platform::class), - array('6', MySQL57Platform::class), - array('10.0.15-MariaDB-1~wheezy', MySqlPlatform::class), - array('5.5.5-10.1.25-MariaDB', MySqlPlatform::class), - array('10.1.2a-MariaDB-a1~lenny-log', MySqlPlatform::class), - array('5.5.40-MariaDB-1~wheezy', MySqlPlatform::class), - array('5.5.40-MariaDB-1~wheezy', MySqlPlatform::class), - array('5.5.5-MariaDB-10.2.8+maria~xenial-log', MariaDb1027Platform::class), - array('10.2.8-MariaDB-10.2.8+maria~xenial-log', MariaDb1027Platform::class), - array('10.2.8-MariaDB-1~lenny-log', MariaDb1027Platform::class) - ); + 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.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() diff --git a/tests/Doctrine/Tests/DBAL/Driver/DrizzlePDOMySql/DriverTest.php b/tests/Doctrine/Tests/DBAL/Driver/DrizzlePDOMySql/DriverTest.php index 49261714e34..004043c2051 100644 --- a/tests/Doctrine/Tests/DBAL/Driver/DrizzlePDOMySql/DriverTest.php +++ b/tests/Doctrine/Tests/DBAL/Driver/DrizzlePDOMySql/DriverTest.php @@ -35,12 +35,12 @@ protected function createSchemaManager(Connection $connection) 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'], + ]; } } diff --git a/tests/Doctrine/Tests/DBAL/Platforms/AbstractMySQLPlatformTestCase.php b/tests/Doctrine/Tests/DBAL/Platforms/AbstractMySQLPlatformTestCase.php index 86d2c6c2a2e..671b5bb8f12 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/AbstractMySQLPlatformTestCase.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/AbstractMySQLPlatformTestCase.php @@ -908,19 +908,4 @@ public function testListTableForeignKeysSQLEvaluatesDatabase() self::assertContains('bar', $sql); self::assertNotContains('DATABASE()', $sql); } - - public function testGetDefaultValueDeclarationSQLIsQuotedWithLiteral() - { - $field = [ - 'type' => Type::getType('string'), - 'default' => "'O'Connor said: \"Hello\" \ \r'" - ]; - - self::assertSame(sprintf( - ' DEFAULT %s', - $this->_platform->quoteStringLiteral("'O'Connor said: \"Hello\" \ \r'") - ), - $this->_platform->getDefaultValueDeclarationSQL($field) - ); - } } diff --git a/tests/Doctrine/Tests/DBAL/Platforms/AbstractPostgreSqlPlatformTestCase.php b/tests/Doctrine/Tests/DBAL/Platforms/AbstractPostgreSqlPlatformTestCase.php index 50e38a15895..c3809f438be 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/AbstractPostgreSqlPlatformTestCase.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/AbstractPostgreSqlPlatformTestCase.php @@ -976,19 +976,4 @@ public function testQuotesDatabaseNameInCloseActiveDatabaseConnectionsSQL() true ); } - - public function testGetDefaultValueDeclarationSQLIsQuotedWithLiteral() - { - $field = [ - 'type' => Type::getType('string'), - 'default' => "'O'Connor said: \"Hello\" \ \r'" - ]; - - self::assertSame(sprintf( - ' DEFAULT %s', - $this->_platform->quoteStringLiteral("'O'Connor said: \"Hello\" \ \r'") - ), - $this->_platform->getDefaultValueDeclarationSQL($field) - ); - } } diff --git a/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php index 94817e3de9d..eb89849b873 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php @@ -12,22 +12,22 @@ class MariaDb1027PlatformTest extends AbstractMySQLPlatformTestCase /** * {@inheritdoc} */ - public function createPlatform() + public function createPlatform() : MariaDb1027Platform { return new MariaDb1027Platform(); } - public function testHasNativeJsonType() + public function testHasNativeJsonType() : void { self::assertTrue($this->_platform->hasNativeJsonType()); } - public function testReturnsJsonTypeDeclarationSQL() + public function testReturnsJsonTypeDeclarationSQL() : void { self::assertSame('JSON', $this->_platform->getJsonTypeDeclarationSQL([])); } - public function testInitializesJsonTypeMapping() + public function testInitializesJsonTypeMapping() : void { self::assertTrue($this->_platform->hasDoctrineTypeMappingFor('json')); self::assertSame(Type::JSON, $this->_platform->getDoctrineTypeMapping('json')); @@ -39,7 +39,7 @@ public function testInitializesJsonTypeMapping() * * @see AbstractMySQLPlatformTestCase::testDoesNotPropagateDefaultValuesForUnsupportedColumnTypes() */ - public function testDoesNotPropagateDefaultValuesForUnsupportedColumnTypes() + public function testDoesNotPropagateDefaultValuesForUnsupportedColumnTypes() : void { $this->markTestSkipped('MariaDB102Platform support propagation of default values for BLOB and TEXT columns'); } @@ -47,7 +47,7 @@ public function testDoesNotPropagateDefaultValuesForUnsupportedColumnTypes() /** * Since MariaDB 10.2, Text and Blob can have a default value. */ - public function testPropagateDefaultValuesForTextAndBlobColumnTypes() + public function testPropagateDefaultValuesForTextAndBlobColumnTypes() : void { $table = new Table("text_blob_default_value"); @@ -69,7 +69,7 @@ public function testPropagateDefaultValuesForTextAndBlobColumnTypes() self::assertFalse($comparator->diffTable($table, $diffTable)); } - public function testPropagateDefaultValuesForJsonColumnType() + public function testPropagateDefaultValuesForJsonColumnType() : void { $table = new Table("text_json_default_value"); diff --git a/tests/Doctrine/Tests/DBAL/Platforms/OraclePlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/OraclePlatformTest.php index 1c0c1c735bd..7ebbea2d11f 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/OraclePlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/OraclePlatformTest.php @@ -874,19 +874,4 @@ public function testQuotesDatabaseNameInListTableColumnsSQL() { self::assertContains("'Foo''Bar\\\\'", $this->_platform->getListTableColumnsSQL('foo_table', "Foo'Bar\\"), '', true); } - - public function testGetDefaultValueDeclarationSQLIsQuotedWithLiteral() - { - $field = [ - 'type' => Type::getType('string'), - 'default' => "'O'Connor said: \"Hello\" \ \r'" - ]; - - self::assertSame(sprintf( - ' DEFAULT %s', - $this->_platform->quoteStringLiteral("'O'Connor said: \"Hello\" \ \r'") - ), - $this->_platform->getDefaultValueDeclarationSQL($field) - ); - } } From 4f876ef07f70d9f33e0fd11fedff72f8a4ad1e3d Mon Sep 17 00:00:00 2001 From: belgattitude Date: Sun, 15 Oct 2017 11:43:47 +0200 Subject: [PATCH 23/45] RegExp change from @Majkl578 suggestion --- lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php index eddd93320e6..947f4894c26 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php @@ -179,12 +179,10 @@ private function getOracleMysqlVersionNumber(string $versionString) : string */ private function getMariaDbMysqlVersionNumber(string $versionString) : string { - $version = str_replace('5.5.5-', '', $versionString); - - if (!preg_match('/^(mariadb-)?(?P\d+)\.(?P\d+)\.(?P\d+)/', strtolower($version), $versionParts)) { + if (!preg_match('/^(?:5\.5\.5-)?(mariadb-)?(?P\d+)\.(?P\d+)\.(?P\d+)/i', $versionString, $versionParts)) { throw DBALException::invalidPlatformVersionSpecified( - $version, - '(mariadb-)?..' + $versionString, + '^(?:5\.5\.5-)?(mariadb-)?..' ); } From d57f69e8a07737dd30e2b5a85b49fd6b2c2d50c4 Mon Sep 17 00:00:00 2001 From: belgattitude Date: Sun, 15 Oct 2017 11:44:05 +0200 Subject: [PATCH 24/45] Updated from @Majkl578 review --- lib/Doctrine/DBAL/Platforms/MySqlPlatform.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php index 6ce6a16f2c1..7255e48db00 100644 --- a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php @@ -468,7 +468,7 @@ protected function _getCreateTableSQL($tableName, array $columns, array $options */ protected function isDefaultValueSupportedForType(Type $field) : bool { - return !($field instanceof TextType || $field instanceof BlobType); + return ! $field instanceof TextType && ! $field instanceof BlobType; } /** From d2543cabd7fef83d256d57f181153a7dec016162 Mon Sep 17 00:00:00 2001 From: belgattitude Date: Mon, 30 Oct 2017 10:26:52 +0100 Subject: [PATCH 25/45] Updated comment for mariadb null handling --- lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index c646c404138..2928c4f0f12 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -216,15 +216,12 @@ protected function _getPortableTableColumnDefinition($tableColumn) * 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 for a what is an expression). + * to distinguish them from expressions (see MDEV-10134). * - CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE are stored in information_schema * as current_timestamp(), currdate(), currtime() * - Literal escaping is normalized in information schema (store "''" instead of "\'") - * - Note: columnDefault should only be null when column is not_nullable otherwise - * a string 'NULL' should be given. Unfortunately, it's not 100% correct in case of - * upgrade from previous versions: see https://jira.mariadb.org/browse/MDEV-14053. - * Otherwise we could simplify the condition === 'NULL' || === null by adding the parameter - * is_nullable. + * - Note: Quoted 'NULL' is not enforced by Maria, it is technically possible to have + * null instead (see https://jira.mariadb.org/browse/MDEV-14053) * * @link https://mariadb.com/kb/en/library/information-schema-columns-table/ * @link https://jira.mariadb.org/browse/MDEV-13132 From b075331d7e588d8d9ab9e9e37a684849d770736f Mon Sep 17 00:00:00 2001 From: belgattitude Date: Mon, 30 Oct 2017 10:27:58 +0100 Subject: [PATCH 26/45] Removes introduced quoting literal support --- .../DBAL/Platforms/AbstractPlatform.php | 4 +- .../DBAL/Platforms/MariaDb1027Platform.php | 3 +- .../Schema/MySqlSchemaManagerTest.php | 102 +----------------- .../Platforms/MariaDb1027PlatformTest.php | 24 +++-- 4 files changed, 24 insertions(+), 109 deletions(-) diff --git a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php index 389a44fba9b..443472807d0 100644 --- a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php @@ -2281,7 +2281,7 @@ public function getDefaultValueDeclarationSQL($field) $default = $field['default']; if ( ! isset($field['type'])) { - return ' DEFAULT ' . $this->quoteStringLiteral($default); + return " DEFAULT '" . $default . "'"; } $type = $field['type']; @@ -2306,7 +2306,7 @@ public function getDefaultValueDeclarationSQL($field) return " DEFAULT '" . $this->convertBooleans($default) . "'"; } - return ' DEFAULT ' . $this->quoteStringLiteral($default); + return " DEFAULT '" . $default . "'"; } /** diff --git a/lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php b/lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php index ad054cdb38e..c43a859fd23 100644 --- a/lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php +++ b/lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php @@ -41,11 +41,12 @@ public function hasNativeJsonType() : bool /** * {@inheritdoc} + * * @link https://mariadb.com/kb/en/library/json-data-type/ */ public function getJsonTypeDeclarationSQL(array $field) : string { - return 'JSON'; + return 'LONGTEXT'; } /** diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index afebc082067..4f38280abe8 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -333,68 +333,6 @@ public function testListFloatTypeColumns() self::assertTrue($columns['col_unsigned']->getUnsigned()); } - /** - * As of MariaDB 10.2.7, nullable default values literals are always single quoted in - * information_schema. Non-nullable defaults behaviour is not affected. - * This test ensure accidental removal of double single encoded defaults for MariaDB >= 10.2.7. - * - * @link https://mariadb.com/kb/en/library/information-schema-columns-table/ - * @link https://dev.mysql.com/doc/refman/5.5/en/string-literals.html - */ - public function testColumnDefaultValuesDoubleQuoted() : void - { - $table = new Table("test_column_default_values_double_quoted"); - $table->addColumn('string_nullable_quoted', 'string', ['notnull' => false, 'default' => 'NULL']); - $table->addColumn('string_nullable_double_quoted', 'string', ['notnull' => false, 'default' => "'NULL'"]); - - $table->addColumn('string_notnull_quoted', 'string', ['notnull' => true, 'default' => 'NULL']); - $table->addColumn('string_notnull_double_quoted', 'string', ['notnull' => true, 'default' => "\\'NULL\\'"]); - - $this->_sm->dropAndCreateTable($table); - - $onlineTable = $this->_sm->listTableDetails("test_column_default_values_double_quoted"); - self::assertSame('NULL', $onlineTable->getColumn('string_nullable_quoted')->getDefault()); - - self::assertSame("'NULL'", $onlineTable->getColumn('string_nullable_double_quoted')->getDefault()); - self::assertSame("NULL", $onlineTable->getColumn('string_notnull_quoted')->getDefault()); - self::assertSame("\\'NULL\\'", $onlineTable->getColumn('string_notnull_double_quoted')->getDefault()); - - $comparator = new Comparator(); - - $diff = $comparator->diffTable($table, $onlineTable); - self::assertFalse($diff, "Tables should be identical with double quoted literals."); - } - - /** - * @link https://mariadb.com/kb/en/library/string-literals - * @link https://dev.mysql.com/doc/refman/5.7/en/string-literals.html - */ - public function testColumnEscapingDefaultValuesDoesNotTriggerSchemaChange() : void - { - $table = new Table("test_column_default_values_escaping"); - $table->addColumn('single_backslash', 'string', ['default' => 'F\Q\D\N']); - $table->addColumn('double_backslash', 'string', ['default' => 'F\\Q\\D\\N']); - $table->addColumn('triple_backslash', 'string', ['default' => 'a\\\z']); - $table->addColumn('repeated_single_quotes', 'string', ['default' => "a''z"]); - $table->addColumn('backslash_double_quote', 'string', ['default' => 'a\"z']); - $table->addColumn('backslash_newline', 'string', ['default' => 'a\nz']); - - $this->_sm->dropAndCreateTable($table); - - $onlineTable = $this->_sm->listTableDetails("test_column_default_values_escaping"); - self::assertSame('F\Q\D\N', $onlineTable->getColumn('single_backslash')->getDefault()); - self::assertSame('F\\Q\\D\\N', $onlineTable->getColumn('double_backslash')->getDefault()); - self::assertSame('a\\\z', $onlineTable->getColumn('triple_backslash')->getDefault()); - self::assertSame("a''z", $onlineTable->getColumn('repeated_single_quotes')->getDefault()); - self::assertSame('a\"z', $onlineTable->getColumn('backslash_double_quote')->getDefault()); - self::assertSame('a\nz', $onlineTable->getColumn('backslash_newline')->getDefault()); - - $comparator = new Comparator(); - - $diff = $comparator->diffTable($table, $onlineTable); - self::assertFalse($diff, "Tables should be identical with values escape sequences."); - } - public function testJsonColumnType() : void { $platform = $this->_sm->getDatabasePlatform(); @@ -411,36 +349,6 @@ public function testJsonColumnType() : void self::assertSame(TYPE::JSON, $columns['col_json']->getType()->getName()); } - /** - * Ensure that a table created outside doctrine and containing - * quoted default values does not trigger a table diff change. I - * Note: MariaDb 10.2 silently change "\'" into "''" when storing in - * information schema, MariaDb102Platform should normalize the table details. - */ - public function testExistingTableWithQuotedDefaultsDoesNotTriggerChange() : void - { - $this->_conn->query('DROP TABLE IF EXISTS test_column_defaults_with_create'); - $sql = " - CREATE TABLE test_column_defaults_with_create ( - col1 VARCHAR(255) NULL DEFAULT 'O''Connor\'\"', - col2 VARCHAR(255) NOT NULL DEFAULT 'O''Connor\'\"' - ); - "; - $this->_conn->query($sql); - - $onlineTable = $this->_sm->listTableDetails("test_column_defaults_with_create"); - self::assertSame("O'Connor'\"", $onlineTable->getColumn('col1')->getDefault()); - self::assertSame("O'Connor'\"", $onlineTable->getColumn('col2')->getDefault()); - - $table = new Table("test_column_defaults_no_diff"); - $table->addColumn('col1', 'string', ['notnull' => false, 'default' => "O'Connor'\""]); - $table->addColumn('col2', 'string', ['notnull' => true, 'default' => "O'Connor'\""]); - - $comparator = new Comparator(); - $diff = $comparator->diffTable($table, $onlineTable); - self::assertFalse($diff); - } - public function testColumnDefaultCurrentTimestamp() : void { $platform = $this->_sm->getDatabasePlatform(); @@ -552,20 +460,20 @@ public function testDoesPropagateDefaultValuesForBlobTextAndJson() : void $table = new Table("text_blob_default_value"); - $json = json_encode(['prop1' => "O'Connor", 'prop2' => 10]); + $json = json_encode(['prop1' => "Hello", 'prop2' => 10]); - $table->addColumn('def_text', 'text', ['default' => "O'Connor"]); + $table->addColumn('def_text', 'text', ['default' => "Hello"]); $table->addColumn('def_text_null', 'text', ['notnull' => false, 'default' => 'def']); - $table->addColumn('def_blob', 'blob', ['default' => '\F\Q\D\N']); + $table->addColumn('def_blob', 'blob', ['default' => 'World']); $table->addColumn('def_json', 'json', ['default' => $json]); $this->_sm->dropAndCreateTable($table); $onlineTable = $this->_sm->listTableDetails("text_blob_default_value"); - self::assertSame("O'Connor", $onlineTable->getColumn('def_text')->getDefault()); + self::assertSame("Hello", $onlineTable->getColumn('def_text')->getDefault()); self::assertSame('def', $onlineTable->getColumn('def_text_null')->getDefault()); - self::assertSame('\F\Q\D\N', $onlineTable->getColumn('def_blob')->getDefault()); + self::assertSame('World', $onlineTable->getColumn('def_blob')->getDefault()); self::assertSame($json, $onlineTable->getColumn('def_json')->getDefault()); $comparator = new Comparator(); diff --git a/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php index eb89849b873..6ff4100cfae 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php @@ -22,9 +22,13 @@ public function testHasNativeJsonType() : void self::assertTrue($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('JSON', $this->_platform->getJsonTypeDeclarationSQL([])); + self::assertSame('LONGTEXT', $this->_platform->getJsonTypeDeclarationSQL([])); } public function testInitializesJsonTypeMapping() : void @@ -51,18 +55,18 @@ public function testPropagateDefaultValuesForTextAndBlobColumnTypes() : void { $table = new Table("text_blob_default_value"); - $table->addColumn('def_text', 'text', array('default' => "d''ef")); - $table->addColumn('def_blob', 'blob', array('default' => 'def')); + $table->addColumn('def_text', Type::TEXT, ['default' => "hello"]); + $table->addColumn('def_blob', Type::BLOB, ['default' => 'world']); self::assertSame( - ["CREATE TABLE text_blob_default_value (def_text LONGTEXT DEFAULT 'd''''ef' NOT NULL, def_blob LONGBLOB DEFAULT 'def' NOT NULL) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB"], + ["CREATE TABLE text_blob_default_value (def_text LONGTEXT DEFAULT 'hello' NOT NULL, def_blob LONGBLOB DEFAULT 'world' NOT NULL) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB"], $this->_platform->getCreateTableSQL($table) ); $diffTable = clone $table; - $diffTable->changeColumn('def_text', ['default' => "d''ef"]); - $diffTable->changeColumn('def_blob', ['default' => 'def']); + $diffTable->changeColumn('def_text', ['default' => "hello"]); + $diffTable->changeColumn('def_blob', ['default' => 'world']); $comparator = new Comparator(); @@ -73,12 +77,14 @@ public function testPropagateDefaultValuesForJsonColumnType() : void { $table = new Table("text_json_default_value"); - $json = json_encode(['prop1' => "O'Connor", 'prop2' => 10]); + $json = json_encode(['prop1' => "Hello", 'prop2' => 10]); + + $table->addColumn('def_json', Type::TEXT, ['default' => $json]); - $table->addColumn('def_json', 'text', ['default' => $json]); + $jsonType = $this->createPlatform()->getJsonTypeDeclarationSQL($table->getColumn('def_json')->toArray()); self::assertSame( - ["CREATE TABLE text_json_default_value (def_json LONGTEXT DEFAULT '{\"prop1\":\"O''Connor\",\"prop2\":10}' NOT NULL) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB"], + ["CREATE TABLE text_json_default_value (def_json $jsonType DEFAULT '{\"prop1\":\"Hello\",\"prop2\":10}' NOT NULL) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB"], $this->_platform->getCreateTableSQL($table) ); From 70924a83e5b9f81b68a8c0dd18cb26b68d225d29 Mon Sep 17 00:00:00 2001 From: belgattitude Date: Mon, 30 Oct 2017 11:02:52 +0100 Subject: [PATCH 27/45] Removed unused use statement --- lib/Doctrine/DBAL/Platforms/MySqlPlatform.php | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php index 7255e48db00..e24f055c99a 100644 --- a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php @@ -26,7 +26,6 @@ use Doctrine\DBAL\Schema\TableDiff; use Doctrine\DBAL\Types\BlobType; use Doctrine\DBAL\Types\TextType; -use Doctrine\DBAL\Types\Type; /** * The MySqlPlatform provides the behavior, features and SQL dialect of the @@ -460,27 +459,15 @@ protected function _getCreateTableSQL($tableName, array $columns, array $options return $sql; } - /** - * Tells whether a field type supports declaration of a default value. - * - * MySQL (as of 5.7.19) does not support default values for Blob and Text - * columns while MariaDB 10.2.1 does. - */ - protected function isDefaultValueSupportedForType(Type $field) : bool - { - return ! $field instanceof TextType && ! $field instanceof BlobType; - } - /** * {@inheritdoc} */ public function getDefaultValueDeclarationSQL($field) { - // Unset the default value if the given field type does not allow default values. - if (! $this->isDefaultValueSupportedForType($field['type'])) { + // Unset the default value if the given field definition does not allow default values. + if ($field['type'] instanceof TextType || $field['type'] instanceof BlobType) { $field['default'] = null; } - return parent::getDefaultValueDeclarationSQL($field); } From 4ee52dbffdfd54b1747c6f6c07743d84fa0810aa Mon Sep 17 00:00:00 2001 From: belgattitude Date: Mon, 30 Oct 2017 11:03:35 +0100 Subject: [PATCH 28/45] Removed unused use statement --- .../Tests/DBAL/Platforms/AbstractMySQLPlatformTestCase.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Doctrine/Tests/DBAL/Platforms/AbstractMySQLPlatformTestCase.php b/tests/Doctrine/Tests/DBAL/Platforms/AbstractMySQLPlatformTestCase.php index 671b5bb8f12..54f49354c52 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/AbstractMySQLPlatformTestCase.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/AbstractMySQLPlatformTestCase.php @@ -8,7 +8,6 @@ use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\TableDiff; -use Doctrine\DBAL\Types\Type; abstract class AbstractMySQLPlatformTestCase extends AbstractPlatformTestCase { From 36b98e27d8da11bef955f140028fd6c35b5646a2 Mon Sep 17 00:00:00 2001 From: belgattitude Date: Mon, 30 Oct 2017 11:13:08 +0100 Subject: [PATCH 29/45] CS fixes --- .../DBAL/Schema/MySqlSchemaManager.php | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index 2928c4f0f12..80139d6edad 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -64,7 +64,7 @@ protected function _getPortableUserDefinition($user) /** * {@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); @@ -121,14 +121,14 @@ protected function _getPortableTableColumnDefinition($tableColumn) $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); } @@ -144,8 +144,8 @@ protected function _getPortableTableColumnDefinition($tableColumn) 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': @@ -198,7 +198,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) ]; if ($scale !== null && $precision !== null) { - $options['scale'] = (int) $scale; + $options['scale'] = (int) $scale; $options['precision'] = (int) $precision; } @@ -211,7 +211,6 @@ protected function _getPortableTableColumnDefinition($tableColumn) return $column; } - /** * Return Doctrine/Mysql-compatible column default values for MariaDB 10.2.7+ servers. * @@ -219,7 +218,6 @@ protected function _getPortableTableColumnDefinition($tableColumn) * to distinguish them from expressions (see MDEV-10134). * - CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE are stored in information_schema * as current_timestamp(), currdate(), currtime() - * - Literal escaping is normalized in information schema (store "''" instead of "\'") * - Note: Quoted 'NULL' is not enforced by Maria, it is technically possible to have * null instead (see https://jira.mariadb.org/browse/MDEV-14053) * @@ -229,19 +227,16 @@ protected function _getPortableTableColumnDefinition($tableColumn) * * @param null|string $columnDefault default value as stored in information_schema for MariaDB >= 10.2.7 */ - private function getMariaDb1027ColumnDefault(MariaDb1027Platform $platform, ?string $columnDefault) : ?string { + 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) - ) - ); + return stripslashes(preg_replace('/^\'(.*)\'$/', '$1', $columnDefault)); } - switch($columnDefault) { + switch ($columnDefault) { case 'current_timestamp()': return $platform->getCurrentTimestampSQL(); case 'curdate()': @@ -260,11 +255,11 @@ protected function _getPortableTableForeignKeysList($tableForeignKeys) $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; } @@ -277,7 +272,7 @@ protected function _getPortableTableForeignKeysList($tableForeignKeys) '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']; } From 9ca9589d31524174794563d1494249f586a7a6d2 Mon Sep 17 00:00:00 2001 From: belgattitude Date: Mon, 30 Oct 2017 11:18:43 +0100 Subject: [PATCH 30/45] CS fixes --- lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php index 947f4894c26..4f342dd7450 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php @@ -137,7 +137,7 @@ public function createDatabasePlatformForVersion($version) return new MariaDb1027Platform(); } - if (!$mariadb && version_compare($this->getOracleMysqlVersionNumber($version), '5.7.9', '>=')) { + if ( ! $mariadb && version_compare($this->getOracleMysqlVersionNumber($version), '5.7.9', '>=')) { return new MySQL57Platform(); } @@ -153,7 +153,7 @@ public function createDatabasePlatformForVersion($version) */ private function getOracleMysqlVersionNumber(string $versionString) : string { - if (!preg_match('/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $versionString, $versionParts)) { + if ( ! preg_match('/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $versionString, $versionParts)) { throw DBALException::invalidPlatformVersionSpecified( $versionString, '..' @@ -179,7 +179,7 @@ private function getOracleMysqlVersionNumber(string $versionString) : string */ private function getMariaDbMysqlVersionNumber(string $versionString) : string { - if (!preg_match('/^(?:5\.5\.5-)?(mariadb-)?(?P\d+)\.(?P\d+)\.(?P\d+)/i', $versionString, $versionParts)) { + if ( ! preg_match('/^(?:5\.5\.5-)?(mariadb-)?(?P\d+)\.(?P\d+)\.(?P\d+)/i', $versionString, $versionParts)) { throw DBALException::invalidPlatformVersionSpecified( $versionString, '^(?:5\.5\.5-)?(mariadb-)?..' From 345b650b7d93914350d9f8c752bdb9b5eb4d051f Mon Sep 17 00:00:00 2001 From: belgattitude Date: Mon, 30 Oct 2017 11:45:25 +0100 Subject: [PATCH 31/45] Removes mariadb supports for TEXT/BLOB default values --- .../DBAL/Platforms/MariaDb1027Platform.php | 11 ----- lib/Doctrine/DBAL/Platforms/MySqlPlatform.php | 2 +- .../Schema/MySqlSchemaManagerTest.php | 34 ------------- .../Platforms/MariaDb1027PlatformTest.php | 49 ------------------- 4 files changed, 1 insertion(+), 95 deletions(-) diff --git a/lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php b/lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php index c43a859fd23..00d01fc25b5 100644 --- a/lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php +++ b/lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php @@ -66,15 +66,4 @@ protected function initializeDoctrineTypeMappings() : void $this->doctrineTypeMapping['json'] = Type::JSON; } - - /** - * @inheritdoc - * - * Since MariaDB 10.2.1 blob and text columns can have a default value. - * @link https://mariadb.com/kb/en/library/blob-and-text-data-types/ - */ - protected function isDefaultValueSupportedForType(Type $field) : bool - { - return true; - } } diff --git a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php index e24f055c99a..5358f471b7c 100644 --- a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php @@ -582,7 +582,7 @@ public function getAlterTableSQL(TableDiff $diff) // Don't propagate default value changes for unsupported column types. if ($columnDiff->hasChanged('default') && count($columnDiff->changedProperties) === 1 && - ! $this->isDefaultValueSupportedForType($columnArray['type']) + ($columnArray['type'] instanceof TextType || $columnArray['type'] instanceof BlobType) ) { continue; } diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index 4f38280abe8..231d43b6e99 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -446,38 +446,4 @@ public function testColumnDefaultValuesCurrentTimeAndDate() : void $diff = $comparator->diffTable($table, $onlineTable); self::assertFalse($diff, "Tables should be identical with column defauts time and date."); } - - /** - * Since MariaDB 10.2.1, Blob and text columns can have a default value - * - * @link https://mariadb.com/kb/en/library/blob-and-text-data-types - */ - public function testDoesPropagateDefaultValuesForBlobTextAndJson() : void - { - if (!$this->_sm->getDatabasePlatform() instanceof MariaDb1027Platform) { - $this->markTestSkipped('Only relevant for MariaDb102Platform.'); - } - - $table = new Table("text_blob_default_value"); - - $json = json_encode(['prop1' => "Hello", 'prop2' => 10]); - - $table->addColumn('def_text', 'text', ['default' => "Hello"]); - $table->addColumn('def_text_null', 'text', ['notnull' => false, 'default' => 'def']); - $table->addColumn('def_blob', 'blob', ['default' => 'World']); - $table->addColumn('def_json', 'json', ['default' => $json]); - - $this->_sm->dropAndCreateTable($table); - - $onlineTable = $this->_sm->listTableDetails("text_blob_default_value"); - - self::assertSame("Hello", $onlineTable->getColumn('def_text')->getDefault()); - self::assertSame('def', $onlineTable->getColumn('def_text_null')->getDefault()); - self::assertSame('World', $onlineTable->getColumn('def_blob')->getDefault()); - self::assertSame($json, $onlineTable->getColumn('def_json')->getDefault()); - - $comparator = new Comparator(); - - self::assertFalse($comparator->diffTable($table, $onlineTable)); - } } diff --git a/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php index 6ff4100cfae..53feccee00b 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php @@ -47,53 +47,4 @@ public function testDoesNotPropagateDefaultValuesForUnsupportedColumnTypes() : v { $this->markTestSkipped('MariaDB102Platform support propagation of default values for BLOB and TEXT columns'); } - - /** - * Since MariaDB 10.2, Text and Blob can have a default value. - */ - public function testPropagateDefaultValuesForTextAndBlobColumnTypes() : void - { - $table = new Table("text_blob_default_value"); - - $table->addColumn('def_text', Type::TEXT, ['default' => "hello"]); - $table->addColumn('def_blob', Type::BLOB, ['default' => 'world']); - - self::assertSame( - ["CREATE TABLE text_blob_default_value (def_text LONGTEXT DEFAULT 'hello' NOT NULL, def_blob LONGBLOB DEFAULT 'world' NOT NULL) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB"], - $this->_platform->getCreateTableSQL($table) - ); - - $diffTable = clone $table; - - $diffTable->changeColumn('def_text', ['default' => "hello"]); - $diffTable->changeColumn('def_blob', ['default' => 'world']); - - $comparator = new Comparator(); - - self::assertFalse($comparator->diffTable($table, $diffTable)); - } - - public function testPropagateDefaultValuesForJsonColumnType() : void - { - $table = new Table("text_json_default_value"); - - $json = json_encode(['prop1' => "Hello", 'prop2' => 10]); - - $table->addColumn('def_json', Type::TEXT, ['default' => $json]); - - $jsonType = $this->createPlatform()->getJsonTypeDeclarationSQL($table->getColumn('def_json')->toArray()); - - self::assertSame( - ["CREATE TABLE text_json_default_value (def_json $jsonType DEFAULT '{\"prop1\":\"Hello\",\"prop2\":10}' NOT NULL) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB"], - $this->_platform->getCreateTableSQL($table) - ); - - $diffTable = clone $table; - - $diffTable->changeColumn('def_json', ['default' => $json]); - - $comparator = new Comparator(); - - self::assertFalse($comparator->diffTable($table, $diffTable)); - } } From 63348cc25c0193b9aa6681d096fb938abcb705d3 Mon Sep 17 00:00:00 2001 From: belgattitude Date: Mon, 30 Oct 2017 12:21:26 +0100 Subject: [PATCH 32/45] CS-FIX --- lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index 80139d6edad..bb8b61af942 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -279,8 +279,10 @@ protected function _getPortableTableForeignKeysList($tableForeignKeys) $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'], From 335d58f9f46a62a3c9050482bf28c464b6192332 Mon Sep 17 00:00:00 2001 From: belgattitude Date: Tue, 31 Oct 2017 13:04:30 +0100 Subject: [PATCH 33/45] BC, removed return types on non-final classes --- lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php | 5 ++--- lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php | 7 ++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php index 4f342dd7450..a00d898dc80 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php @@ -26,7 +26,6 @@ use Doctrine\DBAL\Platforms\MariaDb1027Platform; use Doctrine\DBAL\Platforms\MySQL57Platform; use Doctrine\DBAL\Platforms\MySqlPlatform; -use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\MySqlSchemaManager; use Doctrine\DBAL\VersionAwarePlatformDriver; @@ -207,7 +206,7 @@ public function getDatabase(\Doctrine\DBAL\Connection $conn) * {@inheritdoc} * @return MySqlPlatform */ - public function getDatabasePlatform() : AbstractPlatform + public function getDatabasePlatform() { return new MySqlPlatform(); } @@ -216,7 +215,7 @@ public function getDatabasePlatform() : AbstractPlatform * {@inheritdoc} * @return MySqlSchemaManager */ - public function getSchemaManager(\Doctrine\DBAL\Connection $conn) : AbstractSchemaManager + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) { return new MySqlSchemaManager($conn); } diff --git a/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php b/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php index c4380592170..fcdaab3109f 100644 --- a/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php +++ b/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php @@ -49,15 +49,16 @@ public function connect(array $params, $username = null, $password = null, array /** * {@inheritdoc} */ - public function createDatabasePlatformForVersion($version) : AbstractPlatform + public function createDatabasePlatformForVersion($version) { return $this->getDatabasePlatform(); } /** * {@inheritdoc} + * @return DrizzlePlatform */ - public function getDatabasePlatform() : AbstractPlatform + public function getDatabasePlatform() { return new DrizzlePlatform(); } @@ -65,7 +66,7 @@ public function getDatabasePlatform() : AbstractPlatform /** * {@inheritdoc} */ - public function getSchemaManager(\Doctrine\DBAL\Connection $conn) : AbstractSchemaManager + public function getSchemaManager(\Doctrine\DBAL\Connection $conn) { return new DrizzleSchemaManager($conn); } From 2d14ac5be8e695cf27bf05332ecd4764af5d73af Mon Sep 17 00:00:00 2001 From: belgattitude Date: Tue, 31 Oct 2017 13:13:41 +0100 Subject: [PATCH 34/45] Revert untouched file --- lib/Doctrine/DBAL/Platforms/MySqlPlatform.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php index 5358f471b7c..5d2b6339dac 100644 --- a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php @@ -468,6 +468,7 @@ public function getDefaultValueDeclarationSQL($field) if ($field['type'] instanceof TextType || $field['type'] instanceof BlobType) { $field['default'] = null; } + return parent::getDefaultValueDeclarationSQL($field); } From afee87d07e8a31441c8684a0db8b7c636399b3d4 Mon Sep 17 00:00:00 2001 From: belgattitude Date: Tue, 31 Oct 2017 13:52:35 +0100 Subject: [PATCH 35/45] Revert untouched file --- lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php b/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php index fcdaab3109f..547fdb79570 100644 --- a/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php +++ b/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Driver.php @@ -19,9 +19,7 @@ namespace Doctrine\DBAL\Driver\DrizzlePDOMySql; -use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\DrizzlePlatform; -use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\DrizzleSchemaManager; /** @@ -56,7 +54,6 @@ public function createDatabasePlatformForVersion($version) /** * {@inheritdoc} - * @return DrizzlePlatform */ public function getDatabasePlatform() { From a07f3d31117a893ff84580bda2700b3817c8e77c Mon Sep 17 00:00:00 2001 From: belgattitude Date: Wed, 1 Nov 2017 15:20:35 +0100 Subject: [PATCH 36/45] Removes backslash escaping (still present) --- lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index bb8b61af942..83025357a59 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -219,22 +219,20 @@ protected function _getPortableTableColumnDefinition($tableColumn) * - CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE are stored in information_schema * as current_timestamp(), currdate(), currtime() * - Note: Quoted 'NULL' is not enforced by Maria, it is technically possible to have - * null instead (see https://jira.mariadb.org/browse/MDEV-14053) + * null in some circumstances (see https://jira.mariadb.org/browse/MDEV-14053) * * @link https://mariadb.com/kb/en/library/information-schema-columns-table/ * @link https://jira.mariadb.org/browse/MDEV-13132 - * @link https://jira.mariadb.org/browse/MDEV-14053 * * @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(preg_replace('/^\'(.*)\'$/', '$1', $columnDefault)); + return preg_replace('/^\'(.*)\'$/', '$1', $columnDefault); } switch ($columnDefault) { case 'current_timestamp()': From b1937b0a857a81aa693d4d35aaca0638a7cce7db Mon Sep 17 00:00:00 2001 From: belgattitude Date: Wed, 1 Nov 2017 16:30:37 +0100 Subject: [PATCH 37/45] minor phpdoc fix --- lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php index a00d898dc80..26c77792b99 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php @@ -126,7 +126,6 @@ public function convertException($message, DriverException $exception) /** * {@inheritdoc} * - * @return AbstractPlatform * @throws DBALException */ public function createDatabasePlatformForVersion($version) From 7f2cd3d3bb62e88f5095730d4da67fdc251672e4 Mon Sep 17 00:00:00 2001 From: belgattitude Date: Wed, 1 Nov 2017 21:48:47 +0100 Subject: [PATCH 38/45] Fixed unused use statement --- lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php index 26c77792b99..4dd24ad15ae 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php @@ -22,7 +22,6 @@ use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver; use Doctrine\DBAL\Exception; -use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Platforms\MariaDb1027Platform; use Doctrine\DBAL\Platforms\MySQL57Platform; use Doctrine\DBAL\Platforms\MySqlPlatform; From 45ea287c87451ddb01d8b5bfaf579f3d91ecf874 Mon Sep 17 00:00:00 2001 From: belgattitude Date: Wed, 15 Nov 2017 13:50:31 +0100 Subject: [PATCH 39/45] Unescaping re-added, support limited to schema introspection (#2850 will bring more complete coverage) --- .../DBAL/Schema/MySqlSchemaManager.php | 9 ++++++-- .../Schema/MySqlSchemaManagerTest.php | 21 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index 83025357a59..00e2ba9a4b3 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -218,8 +218,9 @@ protected function _getPortableTableColumnDefinition($tableColumn) * to distinguish them from expressions (see MDEV-10134). * - CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE are stored in information_schema * as current_timestamp(), currdate(), currtime() - * - Note: Quoted 'NULL' is not enforced by Maria, it is technically possible to have + * - 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 @@ -232,7 +233,11 @@ private function getMariaDb1027ColumnDefault(MariaDb1027Platform $platform, ?str return null; } if ($columnDefault[0] === "'") { - return preg_replace('/^\'(.*)\'$/', '$1', $columnDefault); + return stripslashes( + str_replace("''", "'", + preg_replace('/^\'(.*)\'$/', '$1', $columnDefault) + ) + ); } switch ($columnDefault) { case 'current_timestamp()': diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index 231d43b6e99..ebf8675276a 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -446,4 +446,25 @@ public function testColumnDefaultValuesCurrentTimeAndDate() : void $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. + */ + public function testEnsureDefaultsAreUnescapedFromSchemaIntrospection() : void + { + $platform = $this->_sm->getDatabasePlatform(); + $this->_conn->query('DROP TABLE IF EXISTS test_column_defaults_with_create'); + + $default = "a\\0b\\'c\"d\te\\Zf\\\\g''h"; + $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()); + } } From e9c99321eaf53f99a507e0a17bb41a5e02cea629 Mon Sep 17 00:00:00 2001 From: belgattitude Date: Wed, 15 Nov 2017 14:27:56 +0100 Subject: [PATCH 40/45] Improves readability of escape sequences --- .../Schema/MySqlSchemaManagerTest.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index ebf8675276a..6c2e69146b2 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -451,13 +451,29 @@ public function testColumnDefaultValuesCurrentTimeAndDate() : void * 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'); - $default = "a\\0b\\'c\"d\te\\Zf\\\\g''h"; + // https://dev.mysql.com/doc/refman/5.7/en/string-literals.html + $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)} )"; From a04d80409dd64b066dfab17eab6a53aa21fb1bae Mon Sep 17 00:00:00 2001 From: belgattitude Date: Sat, 18 Nov 2017 00:54:25 +0100 Subject: [PATCH 41/45] Fixed CS --- .../DBAL/Driver/AbstractMySQLDriver.php | 12 +++++- .../DBAL/Driver/AbstractMySQLDriverTest.php | 2 +- .../Schema/MySqlSchemaManagerTest.php | 37 ++++++++++--------- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php index 4dd24ad15ae..308c857907a 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php @@ -150,7 +150,11 @@ public function createDatabasePlatformForVersion($version) */ private function getOracleMysqlVersionNumber(string $versionString) : string { - if ( ! preg_match('/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $versionString, $versionParts)) { + if ( ! preg_match( + '/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', + $versionString, + $versionParts + )) { throw DBALException::invalidPlatformVersionSpecified( $versionString, '..' @@ -176,7 +180,11 @@ private function getOracleMysqlVersionNumber(string $versionString) : string */ private function getMariaDbMysqlVersionNumber(string $versionString) : string { - if ( ! preg_match('/^(?:5\.5\.5-)?(mariadb-)?(?P\d+)\.(?P\d+)\.(?P\d+)/i', $versionString, $versionParts)) { + if ( ! preg_match( + '/^(?:5\.5\.5-)?(mariadb-)?(?P\d+)\.(?P\d+)\.(?P\d+)/i', + $versionString, + $versionParts + )) { throw DBALException::invalidPlatformVersionSpecified( $versionString, '^(?:5\.5\.5-)?(mariadb-)?..' diff --git a/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php b/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php index 0aaf76af356..f7039437692 100644 --- a/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php +++ b/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php @@ -54,7 +54,7 @@ protected function createSchemaManager(Connection $connection) return new MySqlSchemaManager($connection); } - protected function getDatabasePlatformsForVersions(): array + protected function getDatabasePlatformsForVersions() : array { return [ ['5.6.9', MySqlPlatform::class], diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index 6c2e69146b2..8680716b4f3 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -17,7 +17,7 @@ protected function setUp() { parent::setUp(); - if (!Type::hasType('point')) { + if ( ! Type::hasType('point')) { Type::addType('point', MySqlPointType::class); } } @@ -30,7 +30,7 @@ public function testSwitchPrimaryKeyColumns() $this->_sm->createTable($tableOld); $tableFetched = $this->_sm->listTableDetails("switch_primary_key_columns"); - $tableNew = clone $tableFetched; + $tableNew = clone $tableFetched; $tableNew->setPrimaryKey(array('bar_id', 'foo_id')); $comparator = new Comparator; @@ -47,7 +47,7 @@ public function testSwitchPrimaryKeyColumns() public function testDiffTableBug() { $schema = new Schema(); - $table = $schema->createTable('diffbug_routing_translations'); + $table = $schema->createTable('diffbug_routing_translations'); $table->addColumn('id', 'integer'); $table->addColumn('route', 'string'); $table->addColumn('locale', 'string'); @@ -62,7 +62,7 @@ public function testDiffTableBug() $tableFetched = $this->_sm->listTableDetails("diffbug_routing_translations"); $comparator = new Comparator; - $diff = $comparator->diffTable($tableFetched, $table); + $diff = $comparator->diffTable($tableFetched, $table); self::assertFalse($diff, "no changes expected."); } @@ -159,7 +159,9 @@ public function testDropPrimaryKeyWithAutoincrementColumn() public function testDoesNotPropagateDefaultValuesForUnsupportedColumnTypes() { if ($this->_sm->getDatabasePlatform() instanceof MariaDb1027Platform) { - $this->markTestSkipped('MariaDb102Platform supports default values for BLOB and TEXT columns and will propagate values'); + $this->markTestSkipped( + 'MariaDb102Platform supports default values for BLOB and TEXT columns and will propagate values' + ); } $table = new Table("text_blob_default_value"); @@ -195,7 +197,7 @@ public function testDoesNotPropagateDefaultValuesForUnsupportedColumnTypes() public function testColumnCollation() { - $table = new Table('test_collation'); + $table = new Table('test_collation'); $table->addOption('collate', $collation = 'latin1_swedish_ci'); $table->addOption('charset', 'latin1'); $table->addColumn('id', 'integer'); @@ -218,7 +220,7 @@ public function testColumnCollation() public function testListLobTypeColumns() { $tableName = 'lob_type_columns'; - $table = new Table($tableName); + $table = new Table($tableName); $table->addColumn('col_tinytext', 'text', array('length' => MySqlPlatform::LENGTH_LIMIT_TINYTEXT)); $table->addColumn('col_text', 'text', array('length' => MySqlPlatform::LENGTH_LIMIT_TEXT)); @@ -232,9 +234,9 @@ public function testListLobTypeColumns() $this->_sm->dropAndCreateTable($table); - $platform = $this->_sm->getDatabasePlatform(); + $platform = $this->_sm->getDatabasePlatform(); $offlineColumns = $table->getColumns(); - $onlineColumns = $this->_sm->listTableColumns($tableName); + $onlineColumns = $this->_sm->listTableColumns($tableName); self::assertSame( $platform->getClobTypeDeclarationSQL($offlineColumns['col_tinytext']->toArray()), @@ -297,7 +299,7 @@ public function testDiffListGuidTableColumn() public function testListDecimalTypeColumns() { $tableName = 'test_list_decimal_columns'; - $table = new Table($tableName); + $table = new Table($tableName); $table->addColumn('col', 'decimal'); $table->addColumn('col_unsigned', 'decimal', array('unsigned' => true)); @@ -318,7 +320,7 @@ public function testListDecimalTypeColumns() public function testListFloatTypeColumns() { $tableName = 'test_list_float_columns'; - $table = new Table($tableName); + $table = new Table($tableName); $table->addColumn('col', 'float'); $table->addColumn('col_unsigned', 'float', array('unsigned' => true)); @@ -336,7 +338,7 @@ public function testListFloatTypeColumns() public function testJsonColumnType() : void { $platform = $this->_sm->getDatabasePlatform(); - if (!$platform->hasNativeJsonType()) { + if ( ! $platform->hasNativeJsonType()) { $this->markTestSkipped("Requires native JSON type"); } @@ -372,7 +374,8 @@ public function testColumnDefaultCurrentTimestamp() : void self::assertFalse($diff, "Tables should be identical with column defaults."); } - public function testColumnDefaultsAreValid() { + public function testColumnDefaultsAreValid() + { $table = new Table("test_column_defaults_are_valid"); @@ -417,7 +420,7 @@ public function testColumnDefaultsAreValid() { */ public function testColumnDefaultValuesCurrentTimeAndDate() : void { - if (!$this->_sm->getDatabasePlatform() instanceof MariaDb1027Platform) { + if ( ! $this->_sm->getDatabasePlatform() instanceof MariaDb1027Platform) { $this->markTestSkipped('Only relevant for MariaDb102Platform.'); } @@ -426,8 +429,8 @@ public function testColumnDefaultValuesCurrentTimeAndDate() : void $table = new Table("test_column_defaults_current_time_and_date"); $currentTimestampSql = $platform->getCurrentTimestampSQL(); - $currentTimeSql = $platform->getCurrentTimeSQL(); - $currentDateSql = $platform->getCurrentDateSQL(); + $currentTimeSql = $platform->getCurrentTimeSQL(); + $currentDateSql = $platform->getCurrentDateSQL(); $table->addColumn('col_datetime', 'datetime', ['default' => $currentTimestampSql]); $table->addColumn('col_date', 'date', ['default' => $currentDateSql]); @@ -472,7 +475,7 @@ public function testEnsureDefaultsAreUnescapedFromSchemaIntrospection() : void '\\%', // A percent (%) character '\\_', // An underscore (_) character ]; - $default = implode('+', $escapeSequences); + $default = implode('+', $escapeSequences); $sql = "CREATE TABLE test_column_defaults_with_create( col1 VARCHAR(255) NULL DEFAULT {$platform->quoteStringLiteral($default)} From 6fa854963d0e27a9dd5698f1ca7c923cbe95fcbf Mon Sep 17 00:00:00 2001 From: belgattitude Date: Sat, 18 Nov 2017 01:44:35 +0100 Subject: [PATCH 42/45] Fixed CS --- .../Schema/MySqlSchemaManagerTest.php | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index 8680716b4f3..392b288a91c 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -17,7 +17,7 @@ protected function setUp() { parent::setUp(); - if ( ! Type::hasType('point')) { + if (!Type::hasType('point')) { Type::addType('point', MySqlPointType::class); } } @@ -30,7 +30,7 @@ public function testSwitchPrimaryKeyColumns() $this->_sm->createTable($tableOld); $tableFetched = $this->_sm->listTableDetails("switch_primary_key_columns"); - $tableNew = clone $tableFetched; + $tableNew = clone $tableFetched; $tableNew->setPrimaryKey(array('bar_id', 'foo_id')); $comparator = new Comparator; @@ -47,7 +47,7 @@ public function testSwitchPrimaryKeyColumns() public function testDiffTableBug() { $schema = new Schema(); - $table = $schema->createTable('diffbug_routing_translations'); + $table = $schema->createTable('diffbug_routing_translations'); $table->addColumn('id', 'integer'); $table->addColumn('route', 'string'); $table->addColumn('locale', 'string'); @@ -62,7 +62,7 @@ public function testDiffTableBug() $tableFetched = $this->_sm->listTableDetails("diffbug_routing_translations"); $comparator = new Comparator; - $diff = $comparator->diffTable($tableFetched, $table); + $diff = $comparator->diffTable($tableFetched, $table); self::assertFalse($diff, "no changes expected."); } @@ -197,7 +197,7 @@ public function testDoesNotPropagateDefaultValuesForUnsupportedColumnTypes() public function testColumnCollation() { - $table = new Table('test_collation'); + $table = new Table('test_collation'); $table->addOption('collate', $collation = 'latin1_swedish_ci'); $table->addOption('charset', 'latin1'); $table->addColumn('id', 'integer'); @@ -220,7 +220,7 @@ public function testColumnCollation() public function testListLobTypeColumns() { $tableName = 'lob_type_columns'; - $table = new Table($tableName); + $table = new Table($tableName); $table->addColumn('col_tinytext', 'text', array('length' => MySqlPlatform::LENGTH_LIMIT_TINYTEXT)); $table->addColumn('col_text', 'text', array('length' => MySqlPlatform::LENGTH_LIMIT_TEXT)); @@ -234,9 +234,9 @@ public function testListLobTypeColumns() $this->_sm->dropAndCreateTable($table); - $platform = $this->_sm->getDatabasePlatform(); + $platform = $this->_sm->getDatabasePlatform(); $offlineColumns = $table->getColumns(); - $onlineColumns = $this->_sm->listTableColumns($tableName); + $onlineColumns = $this->_sm->listTableColumns($tableName); self::assertSame( $platform->getClobTypeDeclarationSQL($offlineColumns['col_tinytext']->toArray()), @@ -299,7 +299,7 @@ public function testDiffListGuidTableColumn() public function testListDecimalTypeColumns() { $tableName = 'test_list_decimal_columns'; - $table = new Table($tableName); + $table = new Table($tableName); $table->addColumn('col', 'decimal'); $table->addColumn('col_unsigned', 'decimal', array('unsigned' => true)); @@ -320,7 +320,7 @@ public function testListDecimalTypeColumns() public function testListFloatTypeColumns() { $tableName = 'test_list_float_columns'; - $table = new Table($tableName); + $table = new Table($tableName); $table->addColumn('col', 'float'); $table->addColumn('col_unsigned', 'float', array('unsigned' => true)); @@ -376,7 +376,6 @@ public function testColumnDefaultCurrentTimestamp() : void public function testColumnDefaultsAreValid() { - $table = new Table("test_column_defaults_are_valid"); $currentTimeStampSql = $this->_sm->getDatabasePlatform()->getCurrentTimestampSQL(); @@ -461,7 +460,6 @@ public function testEnsureDefaultsAreUnescapedFromSchemaIntrospection() : void $platform = $this->_sm->getDatabasePlatform(); $this->_conn->query('DROP TABLE IF EXISTS test_column_defaults_with_create'); - // https://dev.mysql.com/doc/refman/5.7/en/string-literals.html $escapeSequences = [ "\\0", // An ASCII NUL (X'00') character "\\'", "''", // Single quote @@ -475,14 +473,13 @@ public function testEnsureDefaultsAreUnescapedFromSchemaIntrospection() : void '\\%', // 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)} - )"; + $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()); } From 52b4692ebefd5a25420238aa53176ef8d4dc2e83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Cobucci?= Date: Sun, 19 Nov 2017 06:15:13 +0100 Subject: [PATCH 43/45] Disable native JSON support for MariaDB 10.2.x Since the JSON type is simply an alias to a LONG TEXT we must force a comment, otherwise things will not work properly. Also the test for JSON types should work regardless of native JSON support (DBAL should add a comment to identify the correct type). --- lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php | 2 +- .../DBAL/Functional/Schema/MySqlSchemaManagerTest.php | 8 +------- .../Tests/DBAL/Platforms/MariaDb1027PlatformTest.php | 2 +- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php b/lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php index 00d01fc25b5..1955fb05912 100644 --- a/lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php +++ b/lib/Doctrine/DBAL/Platforms/MariaDb1027Platform.php @@ -36,7 +36,7 @@ final class MariaDb1027Platform extends MySqlPlatform */ public function hasNativeJsonType() : bool { - return true; + return false; } /** diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index 392b288a91c..4af8b138b4c 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -12,12 +12,11 @@ class MySqlSchemaManagerTest extends SchemaManagerFunctionalTestCase { - protected function setUp() { parent::setUp(); - if (!Type::hasType('point')) { + if ( ! Type::hasType('point')) { Type::addType('point', MySqlPointType::class); } } @@ -337,11 +336,6 @@ public function testListFloatTypeColumns() public function testJsonColumnType() : void { - $platform = $this->_sm->getDatabasePlatform(); - if ( ! $platform->hasNativeJsonType()) { - $this->markTestSkipped("Requires native JSON type"); - } - $table = new Table('test_mysql_json'); $table->addColumn('col_json', 'json'); $this->_sm->dropAndCreateTable($table); diff --git a/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php index 53feccee00b..79d08d1a07d 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/MariaDb1027PlatformTest.php @@ -19,7 +19,7 @@ public function createPlatform() : MariaDb1027Platform public function testHasNativeJsonType() : void { - self::assertTrue($this->_platform->hasNativeJsonType()); + self::assertFalse($this->_platform->hasNativeJsonType()); } /** From ea56fceca906fb9f5f54c2674ec1d5c16b28558e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Cobucci?= Date: Sun, 19 Nov 2017 12:04:37 +0100 Subject: [PATCH 44/45] Simplify platform detection test Since the test is skipped when the driver is not an instance of `VersionAwarePlatformDriver` we don't need to validate it again. The message was a bit confusing as well (expected vs given). --- .../Tests/DBAL/Driver/AbstractDriverTest.php | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/Doctrine/Tests/DBAL/Driver/AbstractDriverTest.php b/tests/Doctrine/Tests/DBAL/Driver/AbstractDriverTest.php index 740f2f4fc54..dd448d65730 100644 --- a/tests/Doctrine/Tests/DBAL/Driver/AbstractDriverTest.php +++ b/tests/Doctrine/Tests/DBAL/Driver/AbstractDriverTest.php @@ -110,28 +110,28 @@ public function testCreatesDatabasePlatformForVersion() $data = $this->getDatabasePlatformsForVersions(); - if (empty($data)) { - $this->fail( - sprintf( - 'No test data found for test %s. You have to return test data from %s.', - get_class($this) . '::' . __FUNCTION__, - get_class($this) . '::getDatabasePlatformsForVersions' - ) - ); - } + 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) { - self::assertSame($item[1], get_class($this->driver->createDatabasePlatformForVersion($item[0])), + $generatedVersion = get_class($this->driver->createDatabasePlatformForVersion($item[0])); + + self::assertSame( + $item[1], + $generatedVersion, sprintf( - "Expected platform for version %s should be '%s' %s", + 'Expected platform for version "%s" should be "%s", "%s" given', $item[0], $item[1], - ($this->driver instanceof VersionAwarePlatformDriver - ? 'expected: ' . get_class($this->driver->createDatabasePlatformForVersion($item[0])) - : 'see:' . $item[0] - ) - - )); + $generatedVersion + ) + ); } } From 628e84627dfb3ecc14dacf30a3796b81735813cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Cobucci?= Date: Sun, 19 Nov 2017 12:23:14 +0100 Subject: [PATCH 45/45] Remove duplicated entry As per @belgattitude's review. --- tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php b/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php index f7039437692..11fd12a52fb 100644 --- a/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php +++ b/tests/Doctrine/Tests/DBAL/Driver/AbstractMySQLDriverTest.php @@ -68,7 +68,6 @@ protected function getDatabasePlatformsForVersions() : array ['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.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]