Skip to content

Commit

Permalink
Remove deprecated UniqueConstraint features (doctrine#6689)
Browse files Browse the repository at this point in the history
The affected methods, properties and behavior were deprecated in
doctrine#6685.
  • Loading branch information
morozov authored Jan 6, 2025
1 parent 16431d3 commit 22f86e6
Show file tree
Hide file tree
Showing 16 changed files with 156 additions and 471 deletions.
20 changes: 20 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,26 @@ awareness about deprecated code.

# Upgrade to 5.0

## BC BREAK: Changes in `UniqueConstraint` API and behavior

The following `UniqueConstraint` methods and properties have been removed:

- `UniqueConstraint::addColumn()`
- `UniqueConstraint::addFlag()`
- `UniqueConstraint::getColumns()`
- `UniqueConstraint::getFlags()`
- `UniqueConstraint::getOption()`
- `UniqueConstraint::getOptions()`
- `UniqueConstraint::getQuotedColumns()`
- `UniqueConstraint::getUnquotedColumns()`
- `UniqueConstraint::hasFlag()`
- `UniqueConstraint::hasOption()`
- `UniqueConstraint::removeFlag()`
- `UniqueConstraint::$columns`
- `UniqueConstraint::$flags`

Additionally, the `UniqueConstraint` class has been declared as final.

## BC BREAK: Removed `AbstractAsset::isIdentifierQuoted()`

The `AbstractAsset::isIdentifierQuoted()` method has been removed.
Expand Down
22 changes: 0 additions & 22 deletions psalm.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -62,30 +62,8 @@
-->
<referencedMethod name="Doctrine\DBAL\Schema\AbstractAsset::getNameParser" />
<referencedMethod name="Doctrine\DBAL\Schema\AbstractAsset::setName" />

<!--
https://github.com/doctrine/dbal/pull/6685
TODO: remove in 5.0.0
-->
<referencedMethod name="Doctrine\DBAL\Schema\UniqueConstraint::addColumn" />
<referencedMethod name="Doctrine\DBAL\Schema\UniqueConstraint::addFlag" />
<referencedMethod name="Doctrine\DBAL\Schema\UniqueConstraint::getColumns" />
<referencedMethod name="Doctrine\DBAL\Schema\UniqueConstraint::getFlags" />
<referencedMethod name="Doctrine\DBAL\Schema\UniqueConstraint::getOptions" />
<referencedMethod name="Doctrine\DBAL\Schema\UniqueConstraint::getQuotedColumns" />
<referencedMethod name="Doctrine\DBAL\Schema\UniqueConstraint::hasFlag" />
</errorLevel>
</DeprecatedMethod>
<DeprecatedProperty>
<errorLevel type="suppress">
<!--
https://github.com/doctrine/dbal/pull/6685
TODO: remove in 5.0.0
-->
<referencedProperty name="Doctrine\DBAL\Schema\UniqueConstraint::$columns" />
<referencedProperty name="Doctrine\DBAL\Schema\UniqueConstraint::$flags" />
</errorLevel>
</DeprecatedProperty>
<DocblockTypeContradiction>
<errorLevel type="suppress">
<!--
Expand Down
17 changes: 6 additions & 11 deletions src/Platforms/AbstractPlatform.php
Original file line number Diff line number Diff line change
Expand Up @@ -1486,20 +1486,12 @@ public function getCheckDeclarationSQL(array $definition): string
* Obtains DBMS specific DDL fragment that defines a unique constraint to be used in statements like <code>CREATE
* TABLE</code> or <code>ALTER TABLE</code>.
*
* @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy.
*
* @param UniqueConstraint $constraint The unique constraint definition.
*
* @return string DBMS specific DDL fragment that defines the constraint.
*/
public function getUniqueConstraintDeclarationSQL(UniqueConstraint $constraint): string
protected function getUniqueConstraintDeclarationSQL(UniqueConstraint $constraint): string
{
$columns = $constraint->getQuotedColumns($this);

if (count($columns) === 0) {
throw new InvalidArgumentException('Incomplete definition. "columns" required.');
}

$chunks = [];

$name = $constraint->getObjectName();
Expand All @@ -1510,11 +1502,14 @@ public function getUniqueConstraintDeclarationSQL(UniqueConstraint $constraint):

$chunks[] = 'UNIQUE';

if ($constraint->hasFlag('clustered')) {
if ($constraint->isClustered()) {
$chunks[] = 'CLUSTERED';
}

$chunks[] = sprintf('(%s)', implode(', ', $columns));
$chunks[] = sprintf('(%s)', implode(', ', array_map(
fn (UnqualifiedName $columnName) => $columnName->toSQL($this),
$constraint->getColumnNames(),
)));

return implode(' ', $chunks);
}
Expand Down
11 changes: 0 additions & 11 deletions src/Schema/Exception/InvalidState.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,7 @@
use Doctrine\DBAL\Schema\SchemaException;
use LogicException;

use function sprintf;

/** @psalm-immutable */
final class InvalidState extends LogicException implements SchemaException
{
public static function uniqueConstraintHasInvalidColumnNames(string $constraintName): self
{
return new self(sprintf('Unique constraint "%s" has one or more invalid column name.', $constraintName));
}

public static function uniqueConstraintHasEmptyColumnNames(string $constraintName): self
{
return new self(sprintf('Unique constraint "%s" has no column names.', $constraintName));
}
}
116 changes: 73 additions & 43 deletions src/Schema/Table.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,21 @@
use Doctrine\DBAL\Schema\Exception\IndexAlreadyExists;
use Doctrine\DBAL\Schema\Exception\IndexDoesNotExist;
use Doctrine\DBAL\Schema\Exception\IndexNameInvalid;
use Doctrine\DBAL\Schema\Exception\InvalidName;
use Doctrine\DBAL\Schema\Exception\InvalidTableName;
use Doctrine\DBAL\Schema\Exception\PrimaryKeyAlreadyExists;
use Doctrine\DBAL\Schema\Exception\UniqueConstraintDoesNotExist;
use Doctrine\DBAL\Schema\Name\Identifier;
use Doctrine\DBAL\Schema\Name\OptionallyQualifiedName;
use Doctrine\DBAL\Schema\Name\Parser;
use Doctrine\DBAL\Schema\Name\Parser\OptionallyQualifiedNameParser;
use Doctrine\DBAL\Schema\Name\Parsers;
use Doctrine\DBAL\Schema\Name\UnqualifiedName;
use Doctrine\DBAL\Types\Type;
use Doctrine\Deprecations\Deprecation;
use LogicException;

use function array_map;
use function array_merge;
use function array_values;
use function count;
Expand Down Expand Up @@ -145,21 +150,21 @@ public function setPrimaryKey(array $columnNames, ?string $indexName = null): se
/**
* @param non-empty-list<string> $columnNames
* @param array<int, string> $flags
* @param array<string, mixed> $options
*/
public function addUniqueConstraint(
array $columnNames,
?string $indexName = null,
array $flags = [],
array $options = [],
): self {
$indexName ??= $this->_generateIdentifierName(
array_merge([$this->getName()], $columnNames),
'uniq',
$this->maxIdentifierLength,
);

return $this->_addUniqueConstraint($this->_createUniqueConstraint($columnNames, $indexName, $flags, $options));
$isClustered = in_array('clustered', $flags, true);

return $this->_addUniqueConstraint($this->_createUniqueConstraint($columnNames, $indexName, $isClustered));
}

/**
Expand Down Expand Up @@ -692,13 +697,11 @@ protected function _addIndex(Index $index): self

protected function _addUniqueConstraint(UniqueConstraint $constraint): self
{
$columnNames = $constraint->getColumnNames();

$name = $constraint->getName() !== ''
? $constraint->getName()
: $this->_generateIdentifierName(
array_merge((array) $this->getName(), $constraint->getColumns()),
'fk',
$this->maxIdentifierLength,
);
: $this->generateName('fk', $columnNames);

$name = $this->normalizeIdentifier($name);

Expand All @@ -707,13 +710,12 @@ protected function _addUniqueConstraint(UniqueConstraint $constraint): self
// If there is already an index that fulfills this requirements drop the request. In the case of __construct
// calling this method during hydration from schema-details all the explicitly added indexes lead to duplicates.
// This creates computation overhead in this case, however no duplicate indexes are ever added (column based).
$indexName = $this->_generateIdentifierName(
array_merge([$this->getName()], $constraint->getColumns()),
'idx',
$this->maxIdentifierLength,
);
$indexName = $this->generateName('idx', $columnNames);

$indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, true, false);
$indexCandidate = $this->_createIndex(array_map(
static fn (UnqualifiedName $columnName): string => $columnName->toString(),
$columnNames,
), $indexName, true, false);

foreach ($this->_indexes as $existingIndex) {
if ($indexCandidate->isFulfilledBy($existingIndex)) {
Expand Down Expand Up @@ -816,28 +818,43 @@ public function edit(): TableEditor
);
}

/**
* @param non-empty-list<string> $columns
* @param array<int, string> $flags
* @param array<string, mixed> $options
*/
/** @param non-empty-list<string> $columns */
private function _createUniqueConstraint(
array $columns,
string $indexName,
array $flags = [],
array $options = [],
bool $isClustered,
): UniqueConstraint {
if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName)) === 1) {
throw IndexNameInvalid::new($indexName);
}

$parser = Parsers::getUnqualifiedNameParser();

try {
$constraintName = $parser->parse($indexName);
} catch (Parser\Exception $e) {
throw InvalidName::fromParserException($indexName, $e);
}

$columnNames = [];

foreach ($columns as $columnName) {
if (! $this->hasColumn($columnName)) {
throw ColumnDoesNotExist::new($columnName, $this->_name);
}

try {
$columnNames[] = $parser->parse($columnName);
} catch (Parser\Exception $e) {
throw InvalidName::fromParserException($indexName, $e);
}
}

return new UniqueConstraint($indexName, $columns, $flags, $options);
return UniqueConstraint::editor()
->setName($constraintName)
->setColumnNames(...$columnNames)
->setIsClustered($isClustered)
->create();
}

/**
Expand Down Expand Up @@ -866,6 +883,22 @@ private function _createIndex(
return new Index($indexName, $columns, $isUnique, $isPrimary, $flags, $options);
}

/**
* Generates a name from a prefix and a list of column names obeying the configured maximum identifier length.
*
* @param non-empty-list<UnqualifiedName> $columnNames
*/
private function generateName(string $prefix, array $columnNames): string
{
return $this->_generateIdentifierName(
array_merge([$this->getName()], array_map(static function (UnqualifiedName $columnName): string {
return $columnName->getIdentifier()->getValue();
}, $columnNames)),
$prefix,
$this->maxIdentifierLength,
);
}

private function renameColumnInIndexes(string $oldName, string $newName): void
{
foreach ($this->_indexes as $key => $index) {
Expand Down Expand Up @@ -926,28 +959,24 @@ private function renameColumnInForeignKeyConstraints(string $oldName, string $ne
private function renameColumnInUniqueConstraints(string $oldName, string $newName): void
{
foreach ($this->uniqueConstraints as $key => $constraint) {
$modified = false;
$columns = [];
foreach ($constraint->getColumns() as $columnName) {
if ($columnName === $oldName) {
$columns[] = $newName;
$modified = true;
$modified = false;
$columnNames = [];
foreach ($constraint->getColumnNames() as $columnName) {
if ($columnName->getIdentifier()->getValue() === $oldName) {
$columnNames[] = new UnqualifiedName(Identifier::unquoted($newName));
$modified = true;
} else {
$columns[] = $columnName;
$columnNames[] = $columnName;
}
}

if (! $modified) {
continue;
}

/** @psalm-suppress InvalidArgument */
$this->uniqueConstraints[$key] = new UniqueConstraint(
$constraint->getName(),
$columns, // @phpstan-ignore argument.type
$constraint->getFlags(),
$constraint->getOptions(),
);
$this->uniqueConstraints[$key] = $constraint->edit()
->setColumnNames(...$columnNames)
->create();
}
}

Expand All @@ -970,16 +999,17 @@ private function getForeignKeyConstraintNamesByLocalColumnName(string $columnNam
/** @return list<string> */
private function getUniqueConstraintNamesByColumnName(string $columnName): array
{
$names = [];
$constraintNames = [];

foreach ($this->uniqueConstraints as $name => $constraint) {
if (! in_array($columnName, $constraint->getColumns(), true)) {
continue;
foreach ($this->uniqueConstraints as $constraintName => $constraint) {
foreach ($constraint->getColumnNames() as $constraintColumnName) {
if ($constraintColumnName->getIdentifier()->getValue() === $columnName) {
$constraintNames[] = $constraintName;
break;
}
}

$names[] = $name;
}

return $names;
return $constraintNames;
}
}
Loading

0 comments on commit 22f86e6

Please sign in to comment.