Commit 8dadeb7e authored by Steve Müller's avatar Steve Müller Committed by Marco Pivetta

fix renaming indexes used by foreign key constraints

parent 068617eb
......@@ -20,6 +20,7 @@
namespace Doctrine\DBAL\Platforms;
use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Schema\TableDiff;
/**
* Provides the behavior, features and SQL dialect of the MySQL 5.7 database platform.
......@@ -30,6 +31,22 @@ use Doctrine\DBAL\Schema\Index;
*/
class MySQL57Platform extends MySqlPlatform
{
/**
* {@inheritdoc}
*/
protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff)
{
return array();
}
/**
* {@inheritdoc}
*/
protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff)
{
return array();
}
/**
* {@inheritdoc}
*/
......
......@@ -667,7 +667,98 @@ class MySqlPlatform extends AbstractPlatform
$diff->removedForeignKeys = array();
}
$sql = array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff));
$sql = array_merge(
$sql,
parent::getPreAlterTableIndexForeignKeySQL($diff),
$this->getPreAlterTableRenameIndexForeignKeySQL($diff)
);
return $sql;
}
/**
* @param TableDiff $diff The table diff to gather the SQL for.
*
* @return array
*/
protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff)
{
$sql = array();
$tableName = $diff->getName($this)->getQuotedName($this);
foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) {
if (! in_array($foreignKey, $diff->changedForeignKeys, true)) {
$sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
}
}
return $sql;
}
/**
* Returns the remaining foreign key constraints that require one of the renamed indexes.
*
* "Remaining" here refers to the diff between the foreign keys currently defined in the associated
* table and the foreign keys to be removed.
*
* @param TableDiff $diff The table diff to evaluate.
*
* @return array
*/
private function getRemainingForeignKeyConstraintsRequiringRenamedIndexes(TableDiff $diff)
{
if (empty($diff->renamedIndexes) || ! $diff->fromTable instanceof Table) {
return array();
}
$foreignKeys = array();
/** @var \Doctrine\DBAL\Schema\ForeignKeyConstraint[] $remainingForeignKeys */
$remainingForeignKeys = array_diff_key(
$diff->fromTable->getForeignKeys(),
$diff->removedForeignKeys
);
foreach ($remainingForeignKeys as $foreignKey) {
foreach ($diff->renamedIndexes as $index) {
if ($foreignKey->intersectsIndexColumns($index)) {
$foreignKeys[] = $foreignKey;
break;
}
}
}
return $foreignKeys;
}
/**
* {@inheritdoc}
*/
protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff)
{
return array_merge(
parent::getPostAlterTableIndexForeignKeySQL($diff),
$this->getPostAlterTableRenameIndexForeignKeySQL($diff)
);
}
/**
* @param TableDiff $diff The table diff to gather the SQL for.
*
* @return array
*/
protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff)
{
$sql = array();
$tableName = (false !== $diff->newName)
? $diff->getNewName()->getQuotedName($this)
: $diff->getName($this)->getQuotedName($this);
foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) {
if (! in_array($foreignKey, $diff->changedForeignKeys, true)) {
$sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName);
}
}
return $sql;
}
......
......@@ -986,6 +986,12 @@ class SqlitePlatform extends AbstractPlatform
$columnNames = $this->getColumnNamesInAlteredTable($diff);
foreach ($indexes as $key => $index) {
foreach ($diff->renamedIndexes as $oldIndexName => $renamedIndex) {
if (strtolower($key) === strtolower($oldIndexName)) {
unset($indexes[$key]);
}
}
$changed = false;
$indexColumns = array();
foreach ($index->getColumns() as $columnName) {
......
......@@ -363,4 +363,27 @@ class ForeignKeyConstraint extends AbstractAsset implements Constraint
return false;
}
/**
* Checks whether this foreign key constraint intersects the given index columns.
*
* Returns `true` if at least one of this foreign key's local columns
* matches one of the given index's columns, `false` otherwise.
*
* @param Index $index The index to be checked against.
*
* @return boolean
*/
public function intersectsIndexColumns(Index $index)
{
foreach ($index->getColumns() as $indexColumn) {
foreach ($this->_localColumnNames as $localColumn) {
if (strtolower($indexColumn) === strtolower($localColumn->getName())) {
return true;
}
}
}
return false;
}
}
......@@ -557,6 +557,47 @@ class SchemaManagerFunctionalTestCase extends \Doctrine\Tests\DbalFunctionalTest
$this->_sm->alterTable($tableDiff);
}
/**
* @group DBAL-1062
*/
public function testRenameIndexUsedInForeignKeyConstraint()
{
if (! $this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) {
$this->markTestSkipped('This test is only supported on platforms that have foreign keys.');
}
$primaryTable = new Table('test_rename_index_primary');
$primaryTable->addColumn('id', 'integer');
$primaryTable->setPrimaryKey(array('id'));
$foreignTable = new Table('test_rename_index_foreign');
$foreignTable->addColumn('fk', 'integer');
$foreignTable->addIndex(array('fk'), 'rename_index_fk_idx');
$foreignTable->addForeignKeyConstraint(
'test_rename_index_primary',
array('fk'),
array('id'),
array(),
'fk_constraint'
);
$this->_sm->dropAndCreateTable($primaryTable);
$this->_sm->dropAndCreateTable($foreignTable);
$foreignTable2 = clone $foreignTable;
$foreignTable2->renameIndex('rename_index_fk_idx', 'renamed_index_fk_idx');
$comparator = new Comparator();
$this->_sm->alterTable($comparator->diffTable($foreignTable, $foreignTable2));
$foreignTable = $this->_sm->listTableDetails('test_rename_index_foreign');
$this->assertFalse($foreignTable->hasIndex('rename_index_fk_idx'));
$this->assertTrue($foreignTable->hasIndex('renamed_index_fk_idx'));
$this->assertTrue($foreignTable->hasForeignKey('fk_constraint'));
}
/**
* @group DBAL-42
*/
......
......@@ -667,4 +667,17 @@ abstract class AbstractMySQLPlatformTestCase extends AbstractPlatformTestCase
'ALTER TABLE mytable CHANGE name name CHAR(2) NOT NULL',
);
}
/**
* {@inheritdoc}
*/
protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL()
{
return array(
'ALTER TABLE mytable DROP FOREIGN KEY fk_foo',
'DROP INDEX idx_foo ON mytable',
'CREATE INDEX idx_foo_renamed ON mytable (foo)',
'ALTER TABLE mytable ADD CONSTRAINT fk_foo FOREIGN KEY (foo) REFERENCES foreign_table (id)',
);
}
}
......@@ -1159,4 +1159,37 @@ abstract class AbstractPlatformTestCase extends \Doctrine\Tests\DbalTestCase
* @return array
*/
abstract protected function getAlterStringToFixedStringSQL();
/**
* @group DBAL-1062
*/
public function testGeneratesAlterTableRenameIndexUsedByForeignKeySQL()
{
$foreignTable = new Table('foreign_table');
$foreignTable->addColumn('id', 'integer');
$foreignTable->setPrimaryKey(array('id'));
$primaryTable = new Table('mytable');
$primaryTable->addColumn('foo', 'integer');
$primaryTable->addColumn('bar', 'integer');
$primaryTable->addColumn('baz', 'integer');
$primaryTable->addIndex(array('foo'), 'idx_foo');
$primaryTable->addIndex(array('bar'), 'idx_bar');
$primaryTable->addForeignKeyConstraint($foreignTable, array('foo'), array('id'), array(), 'fk_foo');
$primaryTable->addForeignKeyConstraint($foreignTable, array('bar'), array('id'), array(), 'fk_bar');
$tableDiff = new TableDiff('mytable');
$tableDiff->fromTable = $primaryTable;
$tableDiff->renamedIndexes['idx_foo'] = new Index('idx_foo_renamed', array('foo'));
$this->assertSame(
$this->getGeneratesAlterTableRenameIndexUsedByForeignKeySQL(),
$this->_platform->getAlterTableSQL($tableDiff)
);
}
/**
* @return array
*/
abstract protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL();
}
......@@ -765,4 +765,14 @@ abstract class AbstractPostgreSqlPlatformTestCase extends AbstractPlatformTestCa
'ALTER TABLE mytable ALTER name TYPE CHAR(2)',
);
}
/**
* {@inheritdoc}
*/
protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL()
{
return array(
'ALTER INDEX idx_foo RENAME TO idx_foo_renamed',
);
}
}
......@@ -1212,4 +1212,14 @@ abstract class AbstractSQLServerPlatformTestCase extends AbstractPlatformTestCas
'ALTER TABLE mytable ALTER COLUMN name NCHAR(2) NOT NULL',
);
}
/**
* {@inheritdoc}
*/
protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL()
{
return array(
"EXEC sp_RENAME N'mytable.idx_foo', N'idx_foo_renamed', N'INDEX'",
);
}
}
......@@ -650,4 +650,14 @@ class DB2PlatformTest extends AbstractPlatformTestCase
'CALL SYSPROC.ADMIN_CMD (\'REORG TABLE mytable\')',
);
}
/**
* {@inheritdoc}
*/
protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL()
{
return array(
'RENAME INDEX idx_foo TO idx_foo_renamed',
);
}
}
......@@ -55,4 +55,14 @@ class MySQL57PlatformTest extends AbstractMySQLPlatformTestCase
'ALTER TABLE `schema`.`table` RENAME INDEX `foo` TO `bar`',
);
}
/**
* {@inheritdoc}
*/
protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL()
{
return array(
'ALTER TABLE mytable RENAME INDEX idx_foo TO idx_foo_renamed',
);
}
}
......@@ -668,4 +668,14 @@ EOD;
'ALTER TABLE mytable MODIFY (name CHAR(2) DEFAULT NULL)',
);
}
/**
* {@inheritdoc}
*/
protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL()
{
return array(
'ALTER INDEX idx_foo RENAME TO idx_foo_renamed',
);
}
}
......@@ -961,4 +961,14 @@ class SQLAnywherePlatformTest extends AbstractPlatformTestCase
'ALTER TABLE mytable ALTER name CHAR(2) NOT NULL',
);
}
/**
* {@inheritdoc}
*/
protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL()
{
return array(
'ALTER INDEX idx_foo ON mytable RENAME TO idx_foo_renamed',
);
}
}
......@@ -638,4 +638,22 @@ class SqlitePlatformTest extends AbstractPlatformTestCase
'DROP TABLE __temp__mytable',
);
}
/**
* {@inheritdoc}
*/
protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL()
{
return array(
'DROP INDEX idx_foo',
'DROP INDEX idx_bar',
'CREATE TEMPORARY TABLE __temp__mytable AS SELECT foo, bar, baz FROM mytable',
'DROP TABLE mytable',
'CREATE TABLE mytable (foo INTEGER NOT NULL, bar INTEGER NOT NULL, baz INTEGER NOT NULL, CONSTRAINT fk_foo FOREIGN KEY (foo) REFERENCES foreign_table (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT fk_bar FOREIGN KEY (bar) REFERENCES foreign_table (id) NOT DEFERRABLE INITIALLY IMMEDIATE)',
'INSERT INTO mytable (foo, bar, baz) SELECT foo, bar, baz FROM __temp__mytable',
'DROP TABLE __temp__mytable',
'CREATE INDEX idx_bar ON mytable (bar)',
'CREATE INDEX idx_foo_renamed ON mytable (foo)',
);
}
}
<?php
namespace Doctrine\Tests\DBAL\Schema;
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
class ForeignKeyConstraintTest extends \PHPUnit_Framework_TestCase
{
/**
* @group DBAL-1062
*
* @dataProvider getIntersectsIndexColumnsData
*/
public function testIntersectsIndexColumns(array $indexColumns, $expectedResult)
{
$foreignKey = new ForeignKeyConstraint(array('foo', 'bar'), 'foreign_table', array('fk_foo', 'fk_bar'));
$index = $this->getMockBuilder('Doctrine\DBAL\Schema\Index')
->disableOriginalConstructor()
->getMock();
$index->expects($this->once())
->method('getColumns')
->will($this->returnValue($indexColumns));
$this->assertSame($expectedResult, $foreignKey->intersectsIndexColumns($index));
}
/**
* @return array
*/
public function getIntersectsIndexColumnsData()
{
return array(
array(array('baz'), false),
array(array('baz', 'bloo'), false),
array(array('foo'), true),
array(array('bar'), true),
array(array('foo', 'bar'), true),
array(array('bar', 'foo'), true),
array(array('foo', 'baz'), true),
array(array('baz', 'foo'), true),
array(array('bar', 'baz'), true),
array(array('baz', 'bar'), true),
array(array('foo', 'bloo', 'baz'), true),
array(array('bloo', 'foo', 'baz'), true),
array(array('bloo', 'baz', 'foo'), true),
array(array('FOO'), true),
);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment