Skip to content

Commit

Permalink
Merge pull request #6578 from morozov/rename-column-in-indexes-and-co…
Browse files Browse the repository at this point in the history
…nstraints

Rename column in indexes and constraints
  • Loading branch information
morozov authored Oct 31, 2024
2 parents 1103c4f + b35f818 commit 4b084f6
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 0 deletions.
88 changes: 88 additions & 0 deletions src/Schema/Table.php
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,10 @@ final public function renameColumn(string $oldName, string $newName): Column
unset($this->_columns[$oldName]);
$this->_addColumn($column);

$this->renameColumnInIndexes($oldName, $newName);
$this->renameColumnInForeignKeyConstraints($oldName, $newName);
$this->renameColumnInUniqueConstraints($oldName, $newName);

// If a column is renamed multiple times, we only want to know the original and last new name
if (isset($this->renamedColumns[$oldName])) {
$toRemove = $oldName;
Expand Down Expand Up @@ -861,6 +865,90 @@ private function _createIndex(
return new Index($indexName, $columns, $isUnique, $isPrimary, $flags, $options);
}

private function renameColumnInIndexes(string $oldName, string $newName): void
{
foreach ($this->_indexes as $key => $index) {
$modified = false;
$columns = [];
foreach ($index->getColumns() as $columnName) {
if ($columnName === $oldName) {
$columns[] = $newName;
$modified = true;
} else {
$columns[] = $columnName;
}
}

if (! $modified) {
continue;
}

$this->_indexes[$key] = new Index(
$index->getName(),
$columns,
$index->isUnique(),
$index->isPrimary(),
$index->getFlags(),
$index->getOptions(),
);
}
}

private function renameColumnInForeignKeyConstraints(string $oldName, string $newName): void
{
foreach ($this->_fkConstraints as $key => $constraint) {
$modified = false;
$localColumns = [];
foreach ($constraint->getLocalColumns() as $columnName) {
if ($columnName === $oldName) {
$localColumns[] = $newName;
$modified = true;
} else {
$localColumns[] = $columnName;
}
}

if (! $modified) {
continue;
}

$this->_fkConstraints[$key] = new ForeignKeyConstraint(
$localColumns,
$constraint->getForeignTableName(),
$constraint->getForeignColumns(),
$constraint->getName(),
$constraint->getOptions(),
);
}
}

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;
} else {
$columns[] = $columnName;
}
}

if (! $modified) {
continue;
}

$this->uniqueConstraints[$key] = new UniqueConstraint(
$constraint->getName(),
$columns,
$constraint->getFlags(),
$constraint->getOptions(),
);
}
}

/** @return list<string> */
private function getForeignKeyConstraintNamesByLocalColumnName(string $columnName): array
{
Expand Down
76 changes: 76 additions & 0 deletions tests/Functional/Schema/ColumnRenameTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

declare(strict_types=1);

namespace Doctrine\DBAL\Tests\Functional\Schema;

use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Schema\Comparator;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Tests\FunctionalTestCase;
use Doctrine\DBAL\Types\Types;

class ColumnRenameTest extends FunctionalTestCase
{
private AbstractSchemaManager $schemaManager;

private Comparator $comparator;

/** @throws Exception */
protected function setUp(): void
{
$this->schemaManager = $this->connection->createSchemaManager();
$this->comparator = $this->schemaManager->createComparator();
}

/** @throws Exception */
public function testRenameColumnInIndex(): void
{
$this->testRenameColumn(static function (Table $table): void {
$table->addIndex(['c1', 'c2'], 'idx_c1_c2');
});
}

/** @throws Exception */
public function testRenameColumnInForeignKeyConstraint(): void
{
$this->dropTableIfExists('rename_column_referenced');

$referencedTable = new Table('rename_column_referenced');
$referencedTable->addColumn('c1', Types::INTEGER);
$referencedTable->addColumn('c2', Types::INTEGER);

// PostgreSQL requires a unique constraint on the referenced table columns
$referencedTable->addUniqueConstraint(['c1', 'c2']);

$this->connection->createSchemaManager()->createTable($referencedTable);

$this->testRenameColumn(static function (Table $table): void {
$table->addForeignKeyConstraint('rename_column_referenced', ['c1', 'c2'], ['c1', 'c2'], [], 'fk_c1_c2');
});
}

/**
* @param callable(Table): void $modifier
*
* @throws Exception
*/
private function testRenameColumn(callable $modifier): void
{
$this->dropTableIfExists('rename_column');

$table = new Table('rename_column');
$table->addColumn('c1', Types::INTEGER);
$table->addColumn('c2', Types::INTEGER);
$modifier($table);
$table->renameColumn('c1', 'c1a');

$this->connection->createSchemaManager()->createTable($table);

self::assertTrue($this->comparator->compareTables(
$table,
$this->schemaManager->introspectTable('rename_column'),
)->isEmpty());
}
}
30 changes: 30 additions & 0 deletions tests/Schema/TableTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,36 @@ public function testRenameColumnLoop(): void
self::assertCount(0, $table->getRenamedColumns());
}

public function testRenameColumnInIndex(): void
{
$table = new Table('t');
$table->addColumn('c1', Types::INTEGER);
$table->addColumn('c2', Types::INTEGER);
$table->addIndex(['c1', 'c2'], 'idx_c1_c2');
$table->renameColumn('c1', 'c1a');
self::assertSame(['c1a', 'c2'], $table->getIndex('idx_c1_c2')->getColumns());
}

public function testRenameColumnInForeignKeyConstraint(): void
{
$table = new Table('t1');
$table->addColumn('c1', Types::INTEGER);
$table->addColumn('c2', Types::INTEGER);
$table->addForeignKeyConstraint('t2', ['c1', 'c2'], ['c1', 'c2'], [], 'fk_c1_c2');
$table->renameColumn('c2', 'c2a');
self::assertSame(['c1', 'c2a'], $table->getForeignKey('fk_c1_c2')->getLocalColumns());
}

public function testRenameColumnInUniqueConstraint(): void
{
$table = new Table('t');
$table->addColumn('c1', Types::INTEGER);
$table->addColumn('c2', Types::INTEGER);
$table->addUniqueConstraint(['c1', 'c2'], 'uq_c1_c2');
$table->renameColumn('c1', 'c1a');
self::assertSame(['c1a', 'c2'], $table->getUniqueConstraint('uq_c1_c2')->getColumns());
}

public function testColumnsCaseInsensitive(): void
{
$table = new Table('foo');
Expand Down

0 comments on commit 4b084f6

Please sign in to comment.