From b4388a0272a7f4244a422969c66c64c13d98e440 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Wed, 10 Apr 2019 08:10:07 -0700 Subject: [PATCH] Some more changes --- .../DBAL/Platforms/AbstractPlatform.php | 2 +- .../DBAL/Platforms/SQLServerPlatform.php | 2 +- lib/Doctrine/DBAL/Schema/DB2SchemaManager.php | 9 +++- .../DBAL/Schema/MySqlSchemaManager.php | 28 ++++++++++++- .../DBAL/Schema/OracleSchemaManager.php | 9 ++-- .../DBAL/Schema/SQLServerSchemaManager.php | 9 ++-- .../DBAL/Schema/SqliteSchemaManager.php | 9 ++-- .../Schema/MySqlSchemaManagerTest.php | 41 ------------------- 8 files changed, 50 insertions(+), 59 deletions(-) diff --git a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php index acc6af05aa4..5d7c9fdd07f 100644 --- a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php @@ -2311,7 +2311,7 @@ public function getDefaultValueDeclarationSQL($field) return " DEFAULT '" . $this->convertBooleans($default) . "'"; } - return ' DEFAULT ' . $this->quoteDefaultStringLiteral($default); + return ' DEFAULT ' . $this->quoteStringLiteral($default); } /** diff --git a/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php b/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php index a2e68049d68..dc9f88da327 100644 --- a/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php @@ -1629,7 +1629,7 @@ public function getDefaultValueDeclarationSQL($field) return " DEFAULT '" . $this->convertBooleans($field['default']) . "'"; } - return " DEFAULT '" . $field['default'] . "'"; + return ' DEFAULT ' . $this->quoteStringLiteral($field['default']); } /** diff --git a/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php b/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php index 0ae7aba2cf4..201d6015b96 100644 --- a/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php @@ -6,10 +6,11 @@ use const CASE_LOWER; use function array_change_key_case; use function is_resource; +use function preg_match; +use function str_replace; use function strpos; use function strtolower; use function substr; -use function trim; /** * IBM Db2 Schema Manager. @@ -47,7 +48,11 @@ protected function _getPortableTableColumnDefinition($tableColumn) $default = null; if ($tableColumn['default'] !== null && $tableColumn['default'] !== 'NULL') { - $default = trim($tableColumn['default'], "'"); + $default = $tableColumn['default']; + + if (preg_match('/^\'(.*)\'$/s', $default, $matches)) { + $default = str_replace("''", "'", $matches[1]); + } } $type = $this->_platform->getDoctrineTypeMapping($tableColumn['typename']); diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index fb957f247ff..15896b0d7bb 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -7,6 +7,7 @@ use Doctrine\DBAL\Types\Type; use const CASE_LOWER; use function array_change_key_case; +use function array_keys; use function array_shift; use function array_values; use function assert; @@ -14,7 +15,6 @@ use function is_string; use function preg_match; use function str_replace; -use function stripslashes; use function strpos; use function strtok; use function strtolower; @@ -24,6 +24,26 @@ */ class MySqlSchemaManager extends AbstractSchemaManager { + /** + * @see https://mariadb.com/kb/en/library/string-literals/#escape-sequences + */ + private const MARIADB_ESCAPE_SEQUENCES = [ + '\\0' => "\0", + "\\'" => "'", + '\\"' => '"', + '\\b' => "\b", + '\\n' => "\n", + '\\r' => "\r", + '\\t' => "\t", + '\\Z' => "\x1a", + '\\\\' => '\\', + '\\%' => '%', + '\\_' => '_', + + // Internally, MariaDB escapes single quotes using the standard syntax + "''" => "'", + ]; + /** * {@inheritdoc} */ @@ -219,7 +239,11 @@ private function getMariaDb1027ColumnDefault(MariaDb1027Platform $platform, ?str } if (preg_match('/^\'(.*)\'$/', $columnDefault, $matches)) { - return stripslashes(str_replace("''", "'", $matches[1])); + return str_replace( + array_keys(self::MARIADB_ESCAPE_SEQUENCES), + array_values(self::MARIADB_ESCAPE_SEQUENCES), + $matches[1] + ); } switch ($columnDefault) { diff --git a/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php b/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php index d5e103c3007..0e9700a616d 100644 --- a/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php @@ -12,7 +12,6 @@ use function array_values; use function assert; use function preg_match; -use function preg_replace; use function sprintf; use function str_replace; use function strpos; @@ -147,11 +146,9 @@ protected function _getPortableTableColumnDefinition($tableColumn) if ($tableColumn['data_default'] !== null) { // Default values returned from database are represented as literal expressions - $tableColumn['data_default'] = str_replace( - "''", - "'", - preg_replace('/^\'(.*)\'$/s', '$1', $tableColumn['data_default']) - ); + if (preg_match('/^\'(.*)\'$/s', $tableColumn['data_default'], $matches)) { + $tableColumn['data_default'] = str_replace("''", "'", $matches[1]); + } } if ($tableColumn['data_precision'] !== null) { diff --git a/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php b/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php index bd6b02cb9a0..768451d4bf4 100644 --- a/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php @@ -16,7 +16,6 @@ use function str_replace; use function strpos; use function strtok; -use function trim; /** * SQL Server Schema Manager. @@ -126,8 +125,12 @@ protected function _getPortableTableColumnDefinition($tableColumn) private function parseDefaultExpression(string $value) : string { - while (preg_match('/^\((.*)\)$/', $value, $matches)) { - $value = trim($matches[1], "'"); + while (preg_match('/^\((.*)\)$/s', $value, $matches)) { + $value = $matches[1]; + } + + if (preg_match('/^\'(.*)\'$/s', $value, $matches)) { + $value = str_replace("''", "'", $matches[1]); } if ($value === 'getdate()') { diff --git a/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php b/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php index 94c3e936fbd..66d3c8503d5 100644 --- a/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php @@ -324,11 +324,14 @@ protected function _getPortableTableColumnDefinition($tableColumn) if ($default === 'NULL') { $default = null; } + if ($default !== null) { - // SQLite returns strings wrapped in single quotes and escaped, so we need to strip them - $default = preg_replace("/^'(.*)'$/s", '\1', $default); - $default = str_replace("''", "'", $default); + // SQLite returns the default value as a literal expression, so we need to parse it + if (preg_match('/^\'(.*)\'$/s', $default, $matches)) { + $default = str_replace("''", "'", $matches[1]); + } } + $notnull = (bool) $tableColumn['notnull']; if (! isset($tableColumn['name'])) { diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index 92e0f497a6d..6abeee6be0e 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -10,8 +10,6 @@ use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Types\Type; use Doctrine\Tests\Types\MySqlPointType; -use function implode; -use function sprintf; class MySqlSchemaManagerTest extends SchemaManagerFunctionalTestCase { @@ -516,45 +514,6 @@ public function testColumnDefaultValuesCurrentTimeAndDate() : void self::assertFalse($diff, 'Tables should be identical with column defauts time and date.'); } - /** - * Ensure default values (un-)escaping is properly done by mysql platforms. - * The test is voluntarily relying on schema introspection due to current - * doctrine limitations. Once #2850 is landed, this test can be removed. - * - * @see https://dev.mysql.com/doc/refman/5.7/en/string-literals.html - */ - public function testEnsureDefaultsAreUnescapedFromSchemaIntrospection() : void - { - $platform = $this->schemaManager->getDatabasePlatform(); - $this->connection->query('DROP TABLE IF EXISTS test_column_defaults_with_create'); - - $escapeSequences = [ - "\\0", // An ASCII NUL (X'00') character - "\\'", - "''", // Single quote - '\\"', - '""', // Double quote - '\\b', // A backspace character - '\\n', // A new-line character - '\\r', // A carriage return character - '\\t', // A tab character - '\\Z', // ASCII 26 (Control+Z) - '\\\\', // A backslash (\) character - '\\%', // A percent (%) character - '\\_', // An underscore (_) character - ]; - - $default = implode('+', $escapeSequences); - - $sql = sprintf( - 'CREATE TABLE test_column_defaults_with_create(col1 VARCHAR(255) NULL DEFAULT %s)', - $platform->quoteStringLiteral($default) - ); - $this->connection->query($sql); - $onlineTable = $this->schemaManager->listTableDetails('test_column_defaults_with_create'); - self::assertSame($default, $onlineTable->getColumn('col1')->getDefault()); - } - public function testEnsureTableOptionsAreReflectedInMetadata() : void { $this->connection->query('DROP TABLE IF EXISTS test_table_metadata');