Skip to content

Commit 9c540bc

Browse files
committed
fix: quote namespace names
1 parent c91166a commit 9c540bc

File tree

6 files changed

+104
-5
lines changed

6 files changed

+104
-5
lines changed

src/Platforms/AbstractPlatform.php

+13
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
use function key;
5656
use function max;
5757
use function mb_strlen;
58+
use function preg_match;
5859
use function preg_quote;
5960
use function preg_replace;
6061
use function sprintf;
@@ -1038,6 +1039,10 @@ public function getAlterSchemaSQL(SchemaDiff $diff): array
10381039
foreach ($diff->getCreatedSchemas() as $schema) {
10391040
$sql[] = $this->getCreateSchemaSQL($schema);
10401041
}
1042+
1043+
foreach ($diff->getDroppedSchemas() as $schema) {
1044+
$sql[] = $this->getDropSchemaSQL($schema);
1045+
}
10411046
}
10421047

10431048
if ($this->supportsSequences()) {
@@ -1162,6 +1167,10 @@ public function getCreateSchemaSQL(string $schemaName): string
11621167
throw NotSupported::new(__METHOD__);
11631168
}
11641169

1170+
if (preg_match('/^\d/', $schemaName)) {
1171+
return 'CREATE SCHEMA ' . $this->quoteIdentifier($schemaName);
1172+
}
1173+
11651174
return 'CREATE SCHEMA ' . $schemaName;
11661175
}
11671176

@@ -1183,6 +1192,10 @@ public function getDropSchemaSQL(string $schemaName): string
11831192
throw NotSupported::new(__METHOD__);
11841193
}
11851194

1195+
if (preg_match('/^\d/', $schemaName)) {
1196+
return 'DROP SCHEMA ' . $this->quoteIdentifier($schemaName);
1197+
}
1198+
11861199
return 'DROP SCHEMA ' . $schemaName;
11871200
}
11881201

src/Schema/AbstractAsset.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use function dechex;
1212
use function explode;
1313
use function implode;
14+
use function preg_match;
1415
use function str_contains;
1516
use function str_replace;
1617
use function strtolower;
@@ -131,7 +132,9 @@ public function getQuotedName(AbstractPlatform $platform): string
131132
$keywords = $platform->getReservedKeywordsList();
132133
$parts = explode('.', $this->getName());
133134
foreach ($parts as $k => $v) {
134-
$parts[$k] = $this->_quoted || $keywords->isKeyword($v) ? $platform->quoteIdentifier($v) : $v;
135+
$shouldBeQuoted = $this->_quoted || $keywords->isKeyword($v) || preg_match('/^\d/', $v);
136+
137+
$parts[$k] = $shouldBeQuoted ? $platform->quoteIdentifier($v) : $v;
135138
}
136139

137140
return implode('.', $parts);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\DBAL\Schema\Exception;
6+
7+
use Doctrine\DBAL\Schema\SchemaException;
8+
use LogicException;
9+
10+
use function sprintf;
11+
12+
/** @psalm-immutable */
13+
final class NamespaceDoesNotExist extends LogicException implements SchemaException
14+
{
15+
public static function new(string $namespaceName): self
16+
{
17+
return new self(sprintf('There is no namespace with name "%s" in the schema.', $namespaceName));
18+
}
19+
}

src/Schema/Schema.php

+19
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Doctrine\DBAL\Exception;
88
use Doctrine\DBAL\Platforms\AbstractPlatform;
99
use Doctrine\DBAL\Schema\Exception\NamespaceAlreadyExists;
10+
use Doctrine\DBAL\Schema\Exception\NamespaceDoesNotExist;
1011
use Doctrine\DBAL\Schema\Exception\SequenceAlreadyExists;
1112
use Doctrine\DBAL\Schema\Exception\SequenceDoesNotExist;
1213
use Doctrine\DBAL\Schema\Exception\TableAlreadyExists;
@@ -267,6 +268,24 @@ public function createNamespace(string $name): self
267268
return $this;
268269
}
269270

271+
/**
272+
* Drops a new namespace from the schema.
273+
*
274+
* @return $this
275+
*/
276+
public function dropNamespace(string $name): self
277+
{
278+
$unquotedName = strtolower($this->getUnquotedAssetName($name));
279+
280+
if (! isset($this->namespaces[$unquotedName])) {
281+
throw NamespaceDoesNotExist::new($unquotedName);
282+
}
283+
284+
unset($this->namespaces[$unquotedName]);
285+
286+
return $this;
287+
}
288+
270289
/**
271290
* Creates a new table.
272291
*/

tests/Functional/Schema/PostgreSQLSchemaManagerTest.php

+2-4
Original file line numberDiff line numberDiff line change
@@ -336,12 +336,10 @@ protected function assertVarBinaryColumnIsValid(Table $table, string $columnName
336336
/**
337337
* Although this test would pass in isolation on any platform, we keep it here for the following reasons:
338338
*
339-
* 1. The DBAL currently doesn't properly drop tables in the namespaces that need to be quoted
340-
* (@see testListTableDetailsWhenCurrentSchemaNameQuoted()).
341-
* 2. The schema returned by {@see AbstractSchemaManager::introspectSchema()} doesn't contain views, so
339+
* 1. The schema returned by {@see AbstractSchemaManager::introspectSchema()} doesn't contain views, so
342340
* {@see AbstractSchemaManager::dropSchemaObjects()} cannot drop the tables that have dependent views
343341
* (@see testListTablesExcludesViews()).
344-
* 3. In the case of inheritance, PHPUnit runs the tests declared immediately in the test class
342+
* 2. In the case of inheritance, PHPUnit runs the tests declared immediately in the test class
345343
* and then runs the tests declared in the parent.
346344
*
347345
* This test needs to be executed before the ones it conflicts with, so it has to be declared in the same class.

tests/Functional/Schema/SchemaManagerFunctionalTestCase.php

+47
Original file line numberDiff line numberDiff line change
@@ -1419,6 +1419,53 @@ public function testDropColumnWithDefault(): void
14191419
self::assertCount(1, $columns);
14201420
}
14211421

1422+
public function testCanCreateAndDropSchemaThatNeedToBeQuoted(): void
1423+
{
1424+
$platform = $this->connection->getDatabasePlatform();
1425+
1426+
if (! $platform->supportsSchemas()) {
1427+
self::markTestSkipped('The platform does not support schema/namespaces.');
1428+
}
1429+
1430+
$schemaManager = $this->connection->createSchemaManager();
1431+
$schema = $schemaManager->introspectSchema();
1432+
1433+
$schema->createNamespace('001_schema');
1434+
1435+
$schemaManager = $this->connection->createSchemaManager();
1436+
$schemaManager->migrateSchema($schema);
1437+
self::assertContains('001_schema', $schemaManager->listSchemaNames());
1438+
1439+
$schema->dropNamespace('001_schema');
1440+
$schemaManager->migrateSchema($schema);
1441+
self::assertNotContains('001_schema', $schemaManager->listSchemaNames());
1442+
}
1443+
1444+
public function testCanCreateAndDropTableFromNamespaceThatNeedToBeQuoted(): void
1445+
{
1446+
$platform = $this->connection->getDatabasePlatform();
1447+
1448+
if (! $platform->supportsSchemas()) {
1449+
self::markTestSkipped('The platform does not support schema/namespaces.');
1450+
}
1451+
1452+
$schemaManager = $this->connection->createSchemaManager();
1453+
$schema = $schemaManager->introspectSchema();
1454+
1455+
$table = $schema->createTable('001_schema.test_quoted_schema');
1456+
$table->addColumn('id', Types::INTEGER, ['notnull' => true]);
1457+
$table->setPrimaryKey(['id']);
1458+
1459+
$schemaManager = $this->connection->createSchemaManager();
1460+
$schemaManager->migrateSchema($schema);
1461+
self::assertContains('001_schema', $schemaManager->listSchemaNames());
1462+
self::assertContains('001_schema.test_quoted_schema', $schemaManager->listTableNames());
1463+
1464+
$schema->dropTable('001_schema.test_quoted_schema');
1465+
$schemaManager->migrateSchema($schema);
1466+
self::assertNotContains('001_schema.test_quoted_schema', $schemaManager->listTableNames());
1467+
}
1468+
14221469
/** @param list<Table> $tables */
14231470
protected function findTableByName(array $tables, string $name): ?Table
14241471
{

0 commit comments

Comments
 (0)