Skip to content

Commit

Permalink
Merge pull request #2865 from GawainLynch/fix/sqlite-schema-regex
Browse files Browse the repository at this point in the history
Fixes for Sqlite schema regex
  • Loading branch information
lcobucci authored Nov 21, 2017
2 parents 0e8ad2c + 17f1191 commit 954ce2e
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 53 deletions.
48 changes: 17 additions & 31 deletions lib/Doctrine/DBAL/Schema/SqliteSchemaManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ protected function _getPortableTableColumnList($table, $database, $tableColumns)

$comment = $this->parseColumnCommentFromSQL($columnName, $createSql);

if (false !== $comment) {
if ($comment !== null) {
$type = $this->extractDoctrineTypeFromComment($comment, null);

if (null !== $type) {
Expand Down Expand Up @@ -435,43 +435,29 @@ private function getTableDiffForAlterForeignKey(ForeignKeyConstraint $foreignKey
return $tableDiff;
}

/**
* @param string $column
* @param string $sql
*
* @return string|false
*/
private function parseColumnCollationFromSQL($column, $sql)
private function parseColumnCollationFromSQL(string $column, string $sql) : ?string
{
if (preg_match(
'{(?:'.preg_quote($column).'|'.preg_quote($this->_platform->quoteSingleIdentifier($column)).')
[^,(]+(?:\([^()]+\)[^,]*)?
(?:(?:DEFAULT|CHECK)\s*(?:\(.*?\))?[^,]*)*
COLLATE\s+["\']?([^\s,"\')]+)}isx', $sql, $match)) {
return $match[1];
$pattern = '{(?:\W' . preg_quote($column) . '\W|\W' . preg_quote($this->_platform->quoteSingleIdentifier($column))
. '\W)[^,(]+(?:\([^()]+\)[^,]*)?(?:(?:DEFAULT|CHECK)\s*(?:\(.*?\))?[^,]*)*COLLATE\s+["\']?([^\s,"\')]+)}isx';

if (preg_match($pattern, $sql, $match) !== 1) {
return null;
}

return false;
return $match[1];
}

/**
* @param string $column
* @param string $sql
*
* @return string|false
*/
private function parseColumnCommentFromSQL($column, $sql)
private function parseColumnCommentFromSQL(string $column, string $sql) : ?string
{
if (preg_match(
'{[\s(,](?:'.preg_quote($this->_platform->quoteSingleIdentifier($column)).'|'.preg_quote($column).')
(?:\(.*?\)|[^,(])*?,?((?:\s*--[^\n]*\n?)+)
}isx', $sql, $match
)) {
$comment = preg_replace('{^\s*--}m', '', rtrim($match[1], "\n"));

return '' === $comment ? false : $comment;
$pattern = '{[\s(,](?:\W' . preg_quote($this->_platform->quoteSingleIdentifier($column)) . '\W|\W' . preg_quote($column)
. '\W)(?:\(.*?\)|[^,(])*?,?((?:(?!\n))(?:\s*--[^\n]*\n?)+)}ix';

if (preg_match($pattern, $sql, $match) !== 1) {
return null;
}

return false;
$comment = preg_replace('{^\s*--}m', '', rtrim($match[1], "\n"));

return '' === $comment ? null : $comment;
}
}
101 changes: 79 additions & 22 deletions tests/Doctrine/Tests/DBAL/Schema/SqliteSchemaManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,101 @@

namespace Doctrine\Tests\DBAL\Schema;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\DBAL\Schema\SqliteSchemaManager;

class SqliteSchemaManagerTest extends \PHPUnit\Framework\TestCase
{
/**
* @dataProvider getDataColumnCollation
*
* @group 2865
*/
public function testParseColumnCollation($collation, $column, $sql)
public function testParseColumnCollation(?string $collation, string $column, string $sql) : void
{
$conn = $this->getMockBuilder('Doctrine\DBAL\Connection')->disableOriginalConstructor()->getMock();
$conn->expects($this->any())->method('getDatabasePlatform')->will($this->returnValue(new SqlitePlatform()));
$conn = $this->createMock(Connection::class);
$conn->method('getDatabasePlatform')->willReturn(new SqlitePlatform());

$manager = new SqliteSchemaManager($conn);
$ref = new \ReflectionMethod($manager, 'parseColumnCollationFromSQL');
$ref = new \ReflectionMethod($manager, 'parseColumnCollationFromSQL');
$ref->setAccessible(true);

self::assertEquals($collation, $ref->invoke($manager, $column, $sql));
self::assertSame($collation, $ref->invoke($manager, $column, $sql));
}

public function getDataColumnCollation()
{
return array(
array(
'RTRIM', 'a', 'CREATE TABLE "a" ("a" text DEFAULT "aa" COLLATE "RTRIM" NOT NULL)'
),
array(
'utf-8', 'a', 'CREATE TABLE "a" ("b" text UNIQUE NOT NULL COLLATE NOCASE, "a" text DEFAULT "aa" COLLATE "utf-8" NOT NULL)'
),
array(
'NOCASE', 'a', 'CREATE TABLE "a" ("a" text DEFAULT (lower(ltrim(" a") || rtrim("a "))) CHECK ("a") NOT NULL COLLATE NOCASE UNIQUE, "b" text COLLATE RTRIM)'
),
array(
false, 'a', 'CREATE TABLE "a" ("a" text CHECK ("a") NOT NULL, "b" text COLLATE RTRIM)'
),
array(
'RTRIM', 'a"b', 'CREATE TABLE "a" ("a""b" text COLLATE RTRIM)'
),
);
return [
['RTRIM', 'a', 'CREATE TABLE "a" ("a" text DEFAULT "aa" COLLATE "RTRIM" NOT NULL)'],
['utf-8', 'a', 'CREATE TABLE "a" ("b" text UNIQUE NOT NULL COLLATE NOCASE, "a" text DEFAULT "aa" COLLATE "utf-8" NOT NULL)'],
['NOCASE', 'a', 'CREATE TABLE "a" ("a" text DEFAULT (lower(ltrim(" a") || rtrim("a "))) CHECK ("a") NOT NULL COLLATE NOCASE UNIQUE, "b" text COLLATE RTRIM)'],
[null, 'a', 'CREATE TABLE "a" ("a" text CHECK ("a") NOT NULL, "b" text COLLATE RTRIM)'],
['RTRIM', 'a"b', 'CREATE TABLE "a" ("a""b" text COLLATE RTRIM)'],
['BINARY', 'b', 'CREATE TABLE "a" (bb TEXT COLLATE RTRIM, b VARCHAR(42) NOT NULL COLLATE BINARY)'],
['BINARY', 'b', 'CREATE TABLE "a" (bbb TEXT COLLATE NOCASE, bb TEXT COLLATE RTRIM, b VARCHAR(42) NOT NULL COLLATE BINARY)'],
['BINARY', 'b', 'CREATE TABLE "a" (b VARCHAR(42) NOT NULL COLLATE BINARY, bb TEXT COLLATE RTRIM)'],
];
}

/**
* @dataProvider getDataColumnComment
*
* @group 2865
*/
public function testParseColumnCommentFromSQL(?string $comment, string $column, string $sql) : void
{
$conn = $this->createMock(Connection::class);
$conn->method('getDatabasePlatform')->willReturn(new SqlitePlatform());

$manager = new SqliteSchemaManager($conn);
$ref = new \ReflectionMethod($manager, 'parseColumnCommentFromSQL');
$ref->setAccessible(true);

self::assertSame($comment, $ref->invoke($manager, $column, $sql));
}

public function getDataColumnComment()
{
return [
'Single column with no comment' => [
null, 'a', 'CREATE TABLE "a" ("a" TEXT DEFAULT "a" COLLATE RTRIM)',
],
'Single column with type comment' => [
'(DC2Type:x)', 'a', 'CREATE TABLE "a" ("a" CLOB DEFAULT NULL COLLATE BINARY --(DC2Type:x)
)',
],
'Multiple similar columns with type comment 1' => [
null, 'b', 'CREATE TABLE "a" (a TEXT COLLATE RTRIM, "b" TEXT DEFAULT "a" COLLATE RTRIM, "bb" CLOB DEFAULT NULL COLLATE BINARY --(DC2Type:x)
)',
],
'Multiple similar columns with type comment 2' => [
'(DC2Type:x)', 'b', 'CREATE TABLE "a" (a TEXT COLLATE RTRIM, "bb" TEXT DEFAULT "a" COLLATE RTRIM, "b" CLOB DEFAULT NULL COLLATE BINARY --(DC2Type:x)
)',
],
'Multiple similar columns on different lines, with type comment 1' => [
null, 'bb', 'CREATE TABLE "a" (a TEXT COLLATE RTRIM, "b" CLOB DEFAULT NULL COLLATE BINARY --(DC2Type:x)
, "bb" TEXT DEFAULT "a" COLLATE RTRIM',
],
'Multiple similar columns on different lines, with type comment 2' => [
'(DC2Type:x)', 'bb', 'CREATE TABLE "a" (a TEXT COLLATE RTRIM, "bb" CLOB DEFAULT NULL COLLATE BINARY --(DC2Type:x)
, "b" TEXT DEFAULT "a" COLLATE RTRIM',
],
'Column with numeric but no comment 1' => [
null, 'a', 'CREATE TABLE "a" ("a" NUMERIC(10, 0) NOT NULL, "b" CLOB NOT NULL --(DC2Type:array)
, "c" CHAR(36) NOT NULL --(DC2Type:guid)
)',
],
'Column with numeric but no comment 2' => [
null, 'a', 'CREATE TABLE "b" ("a" NUMERIC(10, 0) NOT NULL, "b" CLOB NOT NULL --(DC2Type:array)
, "c" CHAR(36) NOT NULL --(DC2Type:guid)
)',
],
'Column with numeric but no comment 3' => [
'(DC2Type:guid)', 'c', 'CREATE TABLE "b" ("a" NUMERIC(10, 0) NOT NULL, "b" CLOB NOT NULL --(DC2Type:array)
, "c" CHAR(36) NOT NULL --(DC2Type:guid)
)',
],
];
}
}

0 comments on commit 954ce2e

Please sign in to comment.