diff --git a/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php index d74537ff6cc..c6d587956a6 100644 --- a/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php @@ -20,7 +20,6 @@ use function preg_replace; use function sprintf; use function str_replace; -use function stripos; use function strlen; use function strpos; use function strtolower; @@ -329,11 +328,9 @@ protected function _getPortableTableColumnDefinition($tableColumn) $autoincrement = true; } - if (preg_match("/^['(](.*)[')]::.*$/", $tableColumn['default'], $matches)) { + if (preg_match("/^['(](.*)[')]::/", $tableColumn['default'], $matches)) { $tableColumn['default'] = $matches[1]; - } - - if (stripos($tableColumn['default'], 'NULL') === 0) { + } elseif (preg_match('/^NULL::/', $tableColumn['default'])) { $tableColumn['default'] = null; } diff --git a/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php b/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php index 768451d4bf4..31ec6fffd50 100644 --- a/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/SQLServerSchemaManager.php @@ -106,7 +106,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) 'length' => $length === 0 || ! in_array($type, ['text', 'string']) ? null : $length, 'unsigned' => false, 'fixed' => (bool) $fixed, - 'default' => $default !== 'NULL' ? $default : null, + 'default' => $default, 'notnull' => (bool) $tableColumn['notnull'], 'scale' => $tableColumn['scale'], 'precision' => $tableColumn['precision'], @@ -123,12 +123,16 @@ protected function _getPortableTableColumnDefinition($tableColumn) return $column; } - private function parseDefaultExpression(string $value) : string + private function parseDefaultExpression(string $value) : ?string { while (preg_match('/^\((.*)\)$/s', $value, $matches)) { $value = $matches[1]; } + if ($value === 'NULL') { + return null; + } + if (preg_match('/^\'(.*)\'$/s', $value, $matches)) { $value = str_replace("''", "'", $matches[1]); } diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/DefaultValueTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/DefaultValueTest.php new file mode 100644 index 00000000000..2ce09d49945 --- /dev/null +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/DefaultValueTest.php @@ -0,0 +1,143 @@ +addColumn('id', 'integer'); + + foreach (self::columnProvider() as [$name, $default]) { + $table->addColumn($name, 'string', [ + 'default' => $default, + 'notnull' => false, + ]); + } + + $this->connection->getSchemaManager() + ->dropAndCreateTable($table); + + $this->connection->insert('default_value', ['id' => 1]); + } + + /** + * @dataProvider columnProvider + */ + public function testEscapedDefaultValueCanBeIntrospected(string $name, $expectedDefault) : void + { + self::assertSame( + $expectedDefault, + $this->connection + ->getSchemaManager() + ->listTableDetails('default_value') + ->getColumn($name) + ->getDefault() + ); + } + + /** + * @dataProvider columnProvider + */ + public function testEscapedDefaultValueCanBeInserted(string $name, $expectedDefault) : void + { + $value = $this->connection->fetchColumn( + sprintf('SELECT %s FROM default_value', $name) + ); + + self::assertSame($expectedDefault, $value); + } + + /** + * Returns potential escaped literals from all platforms combined. + * + * @see https://dev.mysql.com/doc/refman/5.7/en/string-literals.html + * @see http://www.sqlite.org/lang_expr.html + * @see https://www.postgresql.org/docs/9.6/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS-ESCAPE + * + * @return mixed[][] + */ + public static function columnProvider() : iterable + { + return [ + 'Single quote' => [ + 'single_quote', + "foo'bar", + ], + 'Single quote, doubled' => [ + 'single_quote_doubled', + "foo''bar", + ], + 'Double quote' => [ + 'double_quote', + 'foo"bar', + ], + 'Double quote, doubled' => [ + 'double_quote_doubled', + 'foo""bar', + ], + 'Backspace' => [ + 'backspace', + "foo\x08bar", + ], + 'New line' => [ + 'new_line', + "foo\nbar", + ], + 'Carriage return' => [ + 'carriage_return', + "foo\rbar", + ], + 'Tab' => [ + 'tab', + "foo\tbar", + ], + 'Substitute' => [ + 'substitute', + "foo\x1abar", + ], + 'Backslash' => [ + 'backslash', + 'foo\\bar', + ], + 'Backslash, doubled' => [ + 'backslash_doubled', + 'foo\\\\bar', + ], + 'Percent' => [ + 'percent_sign', + 'foo%bar', + ], + 'Underscore' => [ + 'underscore', + 'foo_bar', + ], + 'NULL string' => [ + 'null_string', + 'NULL', + ], + 'NULL value' => [ + 'null_value', + null, + ], + ]; + } +} diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php index c7e7e5d666d..aeb9f4c90dd 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php @@ -1495,67 +1495,6 @@ public function testCreateAndListSequences() : void self::assertEquals($sequence2InitialValue, $actualSequence2->getInitialValue()); } - /** - * Returns potential escaped literals from all platforms combined. - * - * @see https://dev.mysql.com/doc/refman/5.7/en/string-literals.html - * @see http://www.sqlite.org/lang_expr.html - * @see https://www.postgresql.org/docs/9.6/static/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS-ESCAPE - * - * @return mixed[][] - */ - private function getEscapedLiterals() : iterable - { - return [ - ['An ASCII NUL (X\'00\')', "foo\0bar"], - ['Single quote, C-style', "foo'bar"], - ['Single quote, doubled-style', "foo''bar"], - ['Double quote, C-style', 'foo"bar'], - ['Double quote, double-style', 'foo""bar'], - ['Backspace', "foo\x08bar"], - ['New-line', "foo\nbar"], - ['Carriage return', "foo\rbar"], - ['Tab', "foo\tbar"], - ['ASCII 26 (Control+Z)', "foo\x1abar"], - ['Backslash (\)', 'foo\\bar'], - ['Double backslash', 'foo\\\\bar'], - ['Percent (%)', 'foo%bar'], - ['Underscore (_)', 'foo_bar'], - ]; - } - - private function createTableForDefaultValues() : void - { - $table = new Table('string_escaped_default_value'); - foreach ($this->getEscapedLiterals() as $i => $literal) { - $table->addColumn('field' . $i, 'string', ['default' => $literal[1]]); - } - - $table->addColumn('def_foo', 'string'); - $this->schemaManager->dropAndCreateTable($table); - } - - public function testEscapedDefaultValueCanBeIntrospected() : void - { - $this->createTableForDefaultValues(); - - $onlineTable = $this->schemaManager->listTableDetails('string_escaped_default_value'); - foreach ($this->getEscapedLiterals() as $i => $literal) { - self::assertSame($literal[1], $onlineTable->getColumn('field' . $i)->getDefault(), 'should be able introspect the value of default for: ' . $literal[0]); - } - } - - public function testEscapedDefaultValueCanBeInserted() : void - { - $this->createTableForDefaultValues(); - - $this->connection->insert('string_escaped_default_value', ['def_foo' => 'foo']); - foreach ($this->getEscapedLiterals() as $i => $literal) { - $value = $this->connection->fetchColumn('SELECT field' . $i . ' FROM string_escaped_default_value'); - self::assertSame($literal[1], $value, 'inserted default value should be the configured default value for: ' . $literal[0]); - } - } - /** * @group #3086 */