Skip to content

Commit 4094a55

Browse files
authored
Merge pull request #6870 from morozov/remove-primary-index
Remove support for primary indexes
2 parents ed1f1c9 + b344c97 commit 4094a55

File tree

70 files changed

+1877
-1054
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+1877
-1054
lines changed

UPGRADE.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,19 @@ awareness about deprecated code.
88

99
# Upgrade to 5.0
1010

11+
## BC BREAK: Changes in features related to primary key constraints
12+
13+
1. The `Index` class can no longer represent a primary key constraint. As a result:
14+
1. The `Table::getIndexes()` and `AbstractSchemaManager::listTableIndexes()` methods no longer return the index that
15+
backs the primary key constraint.
16+
2. The index that backs the primary key constraint is no longer considered during implicit index management.
17+
2. The `Table::getPrimaryKey()` and `Table::setPrimaryKey()` methods have been removed.
18+
3. The `Table::renameIndex()` method can no longer be used to rename a primary key constraint.
19+
4. The `AbstractPlatform::getCreatePrimaryKeySQL()` method has been removed.
20+
5. The `PostgreSQLPlatform::getDropIndexSQL()` method no longer builds SQL for dropping a primary key constraint. As a
21+
result, dropping a primary key constraint on Postgres is now only possible if the constraint name is known (e.g. in
22+
the result of database schema introspection).
23+
1124
## BC BREAK: `INTEGER PRIMARY KEY` columns are no longer introspected as auto-incremented on SQLite
1225

1326
Even though `INTEGER PRIMARY KEY` columns are effectively auto-incremented on SQLite, DBAL no longer introspects them as

src/Platforms/AbstractMySQLPlatform.php

Lines changed: 25 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Doctrine\DBAL\Schema\MySQLSchemaManager;
1313
use Doctrine\DBAL\Schema\Name\OptionallyQualifiedName;
1414
use Doctrine\DBAL\Schema\Name\UnquotedIdentifierFolding;
15+
use Doctrine\DBAL\Schema\PrimaryKeyConstraint;
1516
use Doctrine\DBAL\Schema\TableDiff;
1617
use Doctrine\DBAL\TransactionIsolationLevel;
1718
use Doctrine\DBAL\Types\Types;
@@ -24,7 +25,6 @@
2425
use function is_numeric;
2526
use function sprintf;
2627
use function str_replace;
27-
use function strtolower;
2828

2929
/**
3030
* Provides the base implementation for the lowest versions of supported MySQL-like database platforms.
@@ -247,11 +247,8 @@ protected function _getCreateTableSQL(OptionallyQualifiedName $tableName, array
247247
$elements[] = $this->getIndexDeclarationSQL($definition);
248248
}
249249

250-
if (isset($parameters['primary_index'])) {
251-
$elements[] = sprintf(
252-
'PRIMARY KEY (%s)',
253-
implode(', ', $parameters['primary_index']->getQuotedColumns($this)),
254-
);
250+
if (isset($parameters['primaryKey'])) {
251+
$elements[] = $this->getPrimaryKeyConstraintDeclarationSQL($parameters['primaryKey']);
255252
}
256253

257254
$sql = ['CREATE'];
@@ -354,20 +351,14 @@ public function getAlterTableSQL(TableDiff $diff): array
354351
. $this->getColumnDeclarationSQL($newColumnProperties);
355352
}
356353

357-
$droppedIndexes = $this->indexIndexesByLowerCaseName($diff->getDroppedIndexes());
358-
$addedIndexes = $this->indexIndexesByLowerCaseName($diff->getAddedIndexes());
359-
360-
if (isset($droppedIndexes['primary'])) {
354+
if ($diff->getDroppedPrimaryKeyConstraint() !== null) {
361355
$queryParts[] = 'DROP PRIMARY KEY';
362-
363-
$diff->unsetDroppedIndex($droppedIndexes['primary']);
364356
}
365357

366-
if (isset($addedIndexes['primary'])) {
367-
$keyColumns = $addedIndexes['primary']->getQuotedColumns($this);
368-
$queryParts[] = 'ADD PRIMARY KEY (' . implode(', ', $keyColumns) . ')';
358+
$addedPrimaryKeyConstraint = $diff->getAddedPrimaryKeyConstraint();
369359

370-
$diff->unsetAddedIndex($addedIndexes['primary']);
360+
if ($addedPrimaryKeyConstraint !== null) {
361+
$queryParts[] = 'ADD ' . $this->getPrimaryKeyConstraintDeclarationSQL($addedPrimaryKeyConstraint);
371362
}
372363

373364
$tableSql = [];
@@ -399,19 +390,19 @@ protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff): array
399390
continue;
400391
}
401392

402-
$indexClause = 'INDEX ' . $addedIndex->getName();
393+
$indexClause = 'INDEX ' . $addedIndex->getObjectName()->toSQL($this);
403394

404-
if ($addedIndex->isPrimary()) {
405-
$indexClause = 'PRIMARY KEY';
406-
} elseif ($addedIndex->isUnique()) {
407-
$indexClause = 'UNIQUE INDEX ' . $addedIndex->getName();
395+
if ($addedIndex->isUnique()) {
396+
$indexClause = 'UNIQUE ' . $indexClause;
408397
}
409398

410-
$query = 'ALTER TABLE ' . $tableNameSQL . ' DROP INDEX ' . $droppedIndex->getName() . ', ';
411-
$query .= 'ADD ' . $indexClause;
412-
$query .= ' (' . implode(', ', $addedIndex->getQuotedColumns($this)) . ')';
413-
414-
$sql[] = $query;
399+
$sql[] = sprintf(
400+
'ALTER TABLE %s DROP INDEX %s, ADD %s (%s)',
401+
$tableNameSQL,
402+
$droppedIndex->getObjectName()->toSQL($this),
403+
$indexClause,
404+
implode(', ', $addedIndex->getQuotedColumns($this)),
405+
);
415406

416407
$diff->unsetAddedIndex($addedIndex);
417408
$diff->unsetDroppedIndex($droppedIndex);
@@ -529,6 +520,14 @@ protected function getColumnCharsetDeclarationSQL(string $charset): string
529520
return 'CHARACTER SET ' . $charset;
530521
}
531522

523+
protected function getPrimaryKeyConstraintDeclarationSQL(PrimaryKeyConstraint $constraint): string
524+
{
525+
$this->ensurePrimaryKeyConstraintIsNotNamed($constraint);
526+
$this->ensurePrimaryKeyConstraintIsClustered($constraint);
527+
528+
return parent::getPrimaryKeyConstraintDeclarationSQL($constraint);
529+
}
530+
532531
protected function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey): string
533532
{
534533
$query = '';
@@ -665,22 +664,6 @@ public function createSchemaManager(Connection $connection): MySQLSchemaManager
665664
return new MySQLSchemaManager($connection, $this);
666665
}
667666

668-
/**
669-
* @param array<Index> $indexes
670-
*
671-
* @return array<string,Index>
672-
*/
673-
private function indexIndexesByLowerCaseName(array $indexes): array
674-
{
675-
$result = [];
676-
677-
foreach ($indexes as $index) {
678-
$result[strtolower($index->getName())] = $index;
679-
}
680-
681-
return $result;
682-
}
683-
684667
/** @internal The method should be only used from within the {@see MySQLSchemaManager} class hierarchy. */
685668
public function fetchTableOptionsByTable(bool $includeTableName): string
686669
{

src/Platforms/AbstractPlatform.php

Lines changed: 72 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Doctrine\DBAL\LockMode;
1717
use Doctrine\DBAL\Platforms\Exception\NoColumnsSpecifiedForTable;
1818
use Doctrine\DBAL\Platforms\Exception\NotSupported;
19+
use Doctrine\DBAL\Platforms\Exception\UnsupportedPrimaryKeyConstraintDefinition;
1920
use Doctrine\DBAL\Schema\AbstractSchemaManager;
2021
use Doctrine\DBAL\Schema\Column;
2122
use Doctrine\DBAL\Schema\Exception\UnspecifiedConstraintName;
@@ -26,6 +27,7 @@
2627
use Doctrine\DBAL\Schema\Name\OptionallyQualifiedName;
2728
use Doctrine\DBAL\Schema\Name\UnqualifiedName;
2829
use Doctrine\DBAL\Schema\Name\UnquotedIdentifierFolding;
30+
use Doctrine\DBAL\Schema\PrimaryKeyConstraint;
2931
use Doctrine\DBAL\Schema\SchemaDiff;
3032
use Doctrine\DBAL\Schema\Sequence;
3133
use Doctrine\DBAL\Schema\Table;
@@ -42,7 +44,6 @@
4244
use Doctrine\DBAL\Types\Exception\TypeNotFound;
4345
use Doctrine\DBAL\Types\Exception\TypesException;
4446
use Doctrine\DBAL\Types\Type;
45-
use Doctrine\Deprecations\Deprecation;
4647

4748
use function addcslashes;
4849
use function array_map;
@@ -71,8 +72,8 @@
7172
*
7273
* @phpstan-import-type ColumnProperties from Column
7374
* @phpstan-type CreateTableParameters = array{
74-
* primary_index?: Index,
7575
* indexes: list<Index>,
76+
* primaryKey: ?PrimaryKeyConstraint,
7677
* uniqueConstraints: list<UniqueConstraint>,
7778
* foreignKeys: list<ForeignKeyConstraint>,
7879
* comment?: string,
@@ -834,18 +835,13 @@ private function buildCreateTableSQL(Table $table, bool $createForeignKeys): arr
834835

835836
$tableName = $table->getObjectName();
836837
$parameters = $table->getOptions();
838+
$parameters['primaryKey'] = $table->getPrimaryKeyConstraint();
837839
$parameters['indexes'] = [];
838840
$parameters['uniqueConstraints'] = [];
839841
$parameters['foreignKeys'] = [];
840842

841843
foreach ($table->getIndexes() as $index) {
842-
if (! $index->isPrimary()) {
843-
$parameters['indexes'][] = $index;
844-
845-
continue;
846-
}
847-
848-
$parameters['primary_index'] = $index;
844+
$parameters['indexes'][] = $index;
849845
}
850846

851847
foreach ($table->getUniqueConstraints() as $uniqueConstraint) {
@@ -995,11 +991,8 @@ protected function _getCreateTableSQL(OptionallyQualifiedName $tableName, array
995991
$elements[] = $this->getUniqueConstraintDeclarationSQL($definition);
996992
}
997993

998-
if (isset($parameters['primary_index'])) {
999-
$elements[] = sprintf(
1000-
'PRIMARY KEY (%s)',
1001-
implode(', ', $parameters['primary_index']->getQuotedColumns($this)),
1002-
);
994+
if (isset($parameters['primaryKey'])) {
995+
$elements[] = $this->getPrimaryKeyConstraintDeclarationSQL($parameters['primaryKey']);
1003996
}
1004997

1005998
foreach ($parameters['indexes'] as $definition) {
@@ -1114,10 +1107,6 @@ public function getCreateIndexSQL(Index $index, string $table): string
11141107
));
11151108
}
11161109

1117-
if ($index->isPrimary()) {
1118-
return $this->getCreatePrimaryKeySQL($index, $table);
1119-
}
1120-
11211110
$query = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table;
11221111
$query .= ' (' . implode(', ', $index->getQuotedColumns($this)) . ')' . $this->getPartialIndexSQL($index);
11231112

@@ -1144,23 +1133,6 @@ protected function getCreateIndexSQLFlags(Index $index): string
11441133
return $index->isUnique() ? 'UNIQUE ' : '';
11451134
}
11461135

1147-
/**
1148-
* Returns the SQL to create an unnamed primary key constraint.
1149-
*
1150-
* @deprecated
1151-
*/
1152-
public function getCreatePrimaryKeySQL(Index $index, string $table): string
1153-
{
1154-
Deprecation::triggerIfCalledFromOutside(
1155-
'doctrine/dbal',
1156-
'https://github.com/doctrine/dbal/pull/6867',
1157-
'%s() is deprecated.',
1158-
__METHOD__,
1159-
);
1160-
1161-
return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . implode(', ', $index->getQuotedColumns($this)) . ')';
1162-
}
1163-
11641136
/**
11651137
* Returns the SQL to create a named schema.
11661138
*/
@@ -1470,10 +1442,7 @@ protected function getUniqueConstraintDeclarationSQL(UniqueConstraint $constrain
14701442
$chunks[] = 'CLUSTERED';
14711443
}
14721444

1473-
$chunks[] = sprintf('(%s)', implode(', ', array_map(
1474-
fn (UnqualifiedName $columnName) => $columnName->toSQL($this),
1475-
$constraint->getColumnNames(),
1476-
)));
1445+
$chunks[] = $this->buildUnqualifiedNameListSQL($constraint->getColumnNames());
14771446

14781447
return implode(' ', $chunks);
14791448
}
@@ -1506,6 +1475,48 @@ public function getTemporaryTableName(string $tableName): string
15061475
return $tableName;
15071476
}
15081477

1478+
/**
1479+
* Returns declaration of a primary key constraint.
1480+
*/
1481+
protected function getPrimaryKeyConstraintDeclarationSQL(PrimaryKeyConstraint $constraint): string
1482+
{
1483+
$chunks = [];
1484+
1485+
$name = $constraint->getObjectName();
1486+
if ($name !== null) {
1487+
$chunks[] = 'CONSTRAINT';
1488+
$chunks[] = $name->toSQL($this);
1489+
}
1490+
1491+
$chunks[] = 'PRIMARY KEY';
1492+
1493+
if (! $constraint->isClustered()) {
1494+
$chunks[] = 'NONCLUSTERED';
1495+
}
1496+
1497+
$chunks[] = $this->buildUnqualifiedNameListSQL($constraint->getColumnNames());
1498+
1499+
return implode(' ', $chunks);
1500+
}
1501+
1502+
final protected function ensurePrimaryKeyConstraintIsNotNamed(PrimaryKeyConstraint $constraint): void
1503+
{
1504+
if ($constraint->getObjectName() === null) {
1505+
return;
1506+
}
1507+
1508+
throw UnsupportedPrimaryKeyConstraintDefinition::fromNamedConstraint(static::class);
1509+
}
1510+
1511+
final protected function ensurePrimaryKeyConstraintIsClustered(PrimaryKeyConstraint $constraint): void
1512+
{
1513+
if ($constraint->isClustered()) {
1514+
return;
1515+
}
1516+
1517+
throw UnsupportedPrimaryKeyConstraintDefinition::fromNonClusteredConstraint(static::class);
1518+
}
1519+
15091520
/**
15101521
* Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
15111522
* of a column declaration to be used in statements like CREATE TABLE.
@@ -1560,25 +1571,21 @@ protected function getForeignKeyReferentialActionSQL(ReferentialAction $action):
15601571
*/
15611572
protected function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey): string
15621573
{
1563-
$name = $foreignKey->getObjectName();
1574+
$chunks = [];
15641575

1565-
$sql = '';
1576+
$name = $foreignKey->getObjectName();
15661577
if ($name !== null) {
1567-
$sql .= 'CONSTRAINT ' . $name->toSQL($this) . ' ';
1568-
}
1569-
1570-
return $sql . sprintf(
1571-
'FOREIGN KEY (%s) REFERENCES %s (%s)',
1572-
implode(', ', array_map(
1573-
fn (UnqualifiedName $columnName) => $columnName->toSQL($this),
1574-
$foreignKey->getReferencingColumnNames(),
1575-
)),
1576-
$foreignKey->getReferencedTableName()->toSQL($this),
1577-
implode(', ', array_map(
1578-
fn (UnqualifiedName $columnName) => $columnName->toSQL($this),
1579-
$foreignKey->getReferencedColumnNames(),
1580-
)),
1581-
);
1578+
$chunks[] = 'CONSTRAINT';
1579+
$chunks[] = $name->toSQL($this);
1580+
}
1581+
1582+
$chunks[] = 'FOREIGN KEY';
1583+
$chunks[] = $this->buildUnqualifiedNameListSQL($foreignKey->getReferencingColumnNames());
1584+
$chunks[] = 'REFERENCES';
1585+
$chunks[] = $foreignKey->getReferencedTableName()->toSQL($this);
1586+
$chunks[] = $this->buildUnqualifiedNameListSQL($foreignKey->getReferencedColumnNames());
1587+
1588+
return implode(' ', $chunks);
15821589
}
15831590

15841591
/**
@@ -1609,6 +1616,15 @@ protected function getColumnCollationDeclarationSQL(string $collation): string
16091616
return $this->supportsColumnCollation() ? 'COLLATE ' . $this->quoteSingleIdentifier($collation) : '';
16101617
}
16111618

1619+
/** @param non-empty-list<UnqualifiedName> $names */
1620+
private function buildUnqualifiedNameListSQL(array $names): string
1621+
{
1622+
return sprintf('(%s)', implode(', ', array_map(
1623+
fn (UnqualifiedName $columnName) => $columnName->toSQL($this),
1624+
$names,
1625+
)));
1626+
}
1627+
16121628
/**
16131629
* Some platforms need the boolean values to be converted.
16141630
*

0 commit comments

Comments
 (0)