Skip to content

Commit 3cf60fc

Browse files
committed
Fix parsing quoted table name on SQLite
The pattern used in parseTableCommentFromSQL() was invalid. It would wrap the value returned by quoteSingleIdentifier() in another set of quotes. It would only work for unquoted table names, which would be handled but the pattern that matches an unquoted name.
1 parent 9a3d08d commit 3cf60fc

File tree

2 files changed

+43
-8
lines changed

2 files changed

+43
-8
lines changed

src/Schema/SQLiteSchemaManager.php

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Doctrine\DBAL\Types\Type;
1414

1515
use function array_change_key_case;
16+
use function array_map;
1617
use function array_merge;
1718
use function assert;
1819
use function count;
@@ -359,9 +360,8 @@ protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey)
359360

360361
private function parseColumnCollationFromSQL(string $column, string $sql): ?string
361362
{
362-
$pattern = '{(?:\W' . preg_quote($column) . '\W|\W'
363-
. preg_quote($this->platform->quoteSingleIdentifier($column))
364-
. '\W)[^,(]+(?:\([^()]+\)[^,]*)?(?:(?:DEFAULT|CHECK)\s*(?:\(.*?\))?[^,]*)*COLLATE\s+["\']?([^\s,"\')]+)}is';
363+
$pattern = '{' . $this->buildIdentifierPattern($column)
364+
. '[^,(]+(?:\([^()]+\)[^,]*)?(?:(?:DEFAULT|CHECK)\s*(?:\(.*?\))?[^,]*)*COLLATE\s+["\']?([^\s,"\')]+)}is';
365365

366366
if (preg_match($pattern, $sql, $match) !== 1) {
367367
return null;
@@ -373,9 +373,7 @@ private function parseColumnCollationFromSQL(string $column, string $sql): ?stri
373373
private function parseTableCommentFromSQL(string $table, string $sql): ?string
374374
{
375375
$pattern = '/\s* # Allow whitespace characters at start of line
376-
CREATE\sTABLE # Match "CREATE TABLE"
377-
(?:\W"' . preg_quote($this->platform->quoteSingleIdentifier($table), '/') . '"\W|\W' . preg_quote($table, '/')
378-
. '\W) # Match table name (quoted and unquoted)
376+
CREATE\sTABLE' . $this->buildIdentifierPattern($table) . '
379377
( # Start capture
380378
(?:\s*--[^\n]*\n?)+ # Capture anything that starts with whitespaces followed by -- until the end of the line(s)
381379
)/ix';
@@ -391,8 +389,8 @@ private function parseTableCommentFromSQL(string $table, string $sql): ?string
391389

392390
private function parseColumnCommentFromSQL(string $column, string $sql): string
393391
{
394-
$pattern = '{[\s(,](?:\W' . preg_quote($this->platform->quoteSingleIdentifier($column))
395-
. '\W|\W' . preg_quote($column) . '\W)(?:\([^)]*?\)|[^,(])*?,?((?:(?!\n))(?:\s*--[^\n]*\n?)+)}i';
392+
$pattern = '{[\s(,]' . $this->buildIdentifierPattern($column)
393+
. '(?:\([^)]*?\)|[^,(])*?,?((?:(?!\n))(?:\s*--[^\n]*\n?)+)}i';
396394

397395
if (preg_match($pattern, $sql, $match) !== 1) {
398396
return '';
@@ -404,6 +402,22 @@ private function parseColumnCommentFromSQL(string $column, string $sql): string
404402
return $comment;
405403
}
406404

405+
/**
406+
* Returns a regular expression pattern that matches the given unquoted or quoted identifier.
407+
*/
408+
private function buildIdentifierPattern(string $identifier): string
409+
{
410+
return '(?:' . implode('|', array_map(
411+
static function (string $sql): string {
412+
return '\W' . preg_quote($sql, '/') . '\W';
413+
},
414+
[
415+
$identifier,
416+
$this->platform->quoteSingleIdentifier($identifier),
417+
],
418+
)) . ')';
419+
}
420+
407421
/** @throws Exception */
408422
private function getCreateTableSQL(string $table): string
409423
{

tests/Functional/Schema/SQLiteSchemaManagerTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,4 +392,25 @@ public function testShorthandInForeignKeyReferenceWithMultipleColumns(): void
392392
$createTableTrackSql,
393393
);
394394
}
395+
396+
/**
397+
* This test duplicates {@see parent::testCommentInTable()} with the only difference that the name of the table
398+
* being created is quoted. It is only meant to cover the logic of parsing the SQLite CREATE TABLE statement
399+
* when the table name is quoted.
400+
*
401+
* Running the same test for all platforms, on the one hand, won't produce additional coverage, and on the other,
402+
* is not feasible due to the differences in case sensitivity depending on whether the name is quoted.
403+
*
404+
* Once all identifiers are quoted by default, this test can be removed.
405+
*/
406+
public function testCommentInQuotedTable(): void
407+
{
408+
$table = new Table('"table_with_comment"');
409+
$table->addColumn('id', Types::INTEGER);
410+
$table->setComment('This is a comment');
411+
$this->dropAndCreateTable($table);
412+
413+
$table = $this->schemaManager->introspectTable('table_with_comment');
414+
self::assertSame('This is a comment', $table->getComment());
415+
}
395416
}

0 commit comments

Comments
 (0)