Unverified Commit d4fec7cd authored by Guilherme Blanco's avatar Guilherme Blanco Committed by Sergei Morozov

Decouple unique index from unique constraint

parent 9dbec4b5
......@@ -23,6 +23,7 @@ use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Schema\Sequence;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Schema\TableDiff;
use Doctrine\DBAL\Schema\UniqueConstraint;
use Doctrine\DBAL\TransactionIsolationLevel;
use Doctrine\DBAL\Types;
use Doctrine\DBAL\Types\Type;
......@@ -1534,15 +1535,30 @@ abstract class AbstractPlatform
$options['indexes'] = [];
$options['primary'] = [];
if (($createFlags&self::CREATE_INDEXES) > 0) {
if (($createFlags & self::CREATE_INDEXES) > 0) {
foreach ($table->getIndexes() as $index) {
/** @var $index Index */
if ($index->isPrimary()) {
if (! $index->isPrimary()) {
$options['indexes'][$index->getQuotedName($this)] = $index;
continue;
}
$options['primary'] = $index->getQuotedColumns($this);
$options['primary_index'] = $index;
} else {
$options['indexes'][$index->getQuotedName($this)] = $index;
}
foreach ($table->getUniqueConstraints() as $uniqueConstraint) {
/** @var UniqueConstraint $uniqueConstraint */
$options['uniqueConstraints'][$uniqueConstraint->getQuotedName($this)] = $uniqueConstraint;
}
}
if (($createFlags & self::CREATE_FOREIGNKEYS) > 0) {
$options['foreignKeys'] = [];
foreach ($table->getForeignKeys() as $fkConstraint) {
$options['foreignKeys'][] = $fkConstraint;
}
}
......@@ -1551,7 +1567,6 @@ abstract class AbstractPlatform
foreach ($table->getColumns() as $column) {
/** @var Column $column */
if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn)) {
$eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this);
$this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs);
......@@ -1579,13 +1594,6 @@ abstract class AbstractPlatform
$columns[$columnData['name']] = $columnData;
}
if (($createFlags&self::CREATE_FOREIGNKEYS) > 0) {
$options['foreignKeys'] = [];
foreach ($table->getForeignKeys() as $fkConstraint) {
$options['foreignKeys'][] = $fkConstraint;
}
}
if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) {
$eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this);
$this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs);
......
......@@ -893,10 +893,11 @@ class SqlitePlatform extends AbstractPlatform
$sql = [];
$tableSql = [];
if (! $this->onSchemaAlterTable($diff, $tableSql)) {
$dataTable = new Table('__temp__' . $table->getName());
$newTable = new Table($table->getQuotedName($this), $columns, $this->getPrimaryIndexInAlteredTable($diff), $this->getForeignKeysInAlteredTable($diff), 0, $table->getOptions());
$newTable = new Table($table->getQuotedName($this), $columns, $this->getPrimaryIndexInAlteredTable($diff), [], $this->getForeignKeysInAlteredTable($diff), 0, $table->getOptions());
$newTable->addOption('alter', true);
$sql = $this->getPreAlterTableIndexForeignKeySQL($diff);
......
......@@ -263,12 +263,14 @@ abstract class AbstractSchemaManager
{
$columns = $this->listTableColumns($tableName);
$foreignKeys = [];
if ($this->_platform->supportsForeignKeyConstraints()) {
$foreignKeys = $this->listTableForeignKeys($tableName);
}
$indexes = $this->listTableIndexes($tableName);
return new Table($tableName, $columns, $indexes, $foreignKeys, false, []);
return new Table($tableName, $columns, $indexes, [], $foreignKeys, false, []);
}
/**
......@@ -587,6 +589,7 @@ abstract class AbstractSchemaManager
public function alterTable(TableDiff $tableDiff)
{
$queries = $this->_platform->getAlterTableSQL($tableDiff);
if (! is_array($queries) || ! count($queries)) {
return;
}
......
......@@ -57,6 +57,7 @@ class Index extends AbstractAsset implements Constraint
$isUnique = $isUnique || $isPrimary;
$this->_setName($indexName);
$this->_isUnique = $isUnique;
$this->_isPrimary = $isPrimary;
$this->options = $options;
......@@ -64,6 +65,7 @@ class Index extends AbstractAsset implements Constraint
foreach ($columns as $column) {
$this->_addColumn($column);
}
foreach ($flags as $flag) {
$this->addFlag($flag);
}
......
......@@ -18,7 +18,8 @@ class SchemaException extends DBALException
public const SEQUENCE_ALREADY_EXISTS = 80;
public const INDEX_INVALID_NAME = 90;
public const FOREIGNKEY_DOESNT_EXIST = 100;
public const NAMESPACE_ALREADY_EXISTS = 110;
public const CONSTRAINT_DOESNT_EXIST = 110;
public const NAMESPACE_ALREADY_EXISTS = 120;
/**
* @param string $tableName
......@@ -142,6 +143,21 @@ class SchemaException extends DBALException
return new self("There exists no sequence with the name '" . $sequenceName . "'.", self::SEQUENCE_DOENST_EXIST);
}
/**
* @param string $constraintName
* @param string $table
*
* @return self
*/
public static function uniqueConstraintDoesNotExist($constraintName, $table)
{
return new self(sprintf(
'There exists no unique constraint with the name "%s" on table "%s".',
$constraintName,
$table
), self::CONSTRAINT_DOESNT_EXIST);
}
/**
* @param string $fkName
* @param string $table
......
......@@ -7,7 +7,9 @@ use Doctrine\DBAL\Schema\Visitor\Visitor;
use Doctrine\DBAL\Types\Type;
use const ARRAY_FILTER_USE_KEY;
use function array_filter;
use function array_keys;
use function array_merge;
use function array_unique;
use function in_array;
use function is_numeric;
use function is_string;
......@@ -35,6 +37,9 @@ class Table extends AbstractAsset
/** @var string */
protected $_primaryKeyName = false;
/** @var UniqueConstraint[] */
protected $_uniqueConstraints = [];
/** @var ForeignKeyConstraint[] */
protected $_fkConstraints = [];
......@@ -48,14 +53,22 @@ class Table extends AbstractAsset
* @param string $tableName
* @param Column[] $columns
* @param Index[] $indexes
* @param UniqueConstraint[] $uniqueConstraints
* @param ForeignKeyConstraint[] $fkConstraints
* @param int $idGeneratorType
* @param mixed[] $options
*
* @throws DBALException
*/
public function __construct($tableName, array $columns = [], array $indexes = [], array $fkConstraints = [], $idGeneratorType = 0, array $options = [])
{
public function __construct(
$tableName,
array $columns = [],
array $indexes = [],
array $uniqueConstraints = [],
array $fkConstraints = [],
$idGeneratorType = 0,
array $options = []
) {
if (strlen($tableName) === 0) {
throw DBALException::invalidTableName($tableName);
}
......@@ -70,8 +83,12 @@ class Table extends AbstractAsset
$this->_addIndex($idx);
}
foreach ($fkConstraints as $constraint) {
$this->_addForeignKeyConstraint($constraint);
foreach ($uniqueConstraints as $uniqueConstraint) {
$this->_addUniqueConstraint($uniqueConstraint);
}
foreach ($fkConstraints as $fkConstraint) {
$this->_addForeignKeyConstraint($fkConstraint);
}
$this->_options = $options;
......@@ -85,18 +102,6 @@ class Table extends AbstractAsset
$this->_schemaConfig = $schemaConfig;
}
/**
* @return int
*/
protected function _getMaxIdentifierLength()
{
if ($this->_schemaConfig instanceof SchemaConfig) {
return $this->_schemaConfig->getMaxIdentifierLength();
}
return 63;
}
/**
* Sets the Primary Key.
*
......@@ -117,6 +122,26 @@ class Table extends AbstractAsset
return $this;
}
/**
* @param mixed[] $columnNames
* @param string|null $indexName
* @param mixed[] $options
*
* @return self
*/
public function addUniqueConstraint(array $columnNames, $indexName = null, array $options = [])
{
if ($indexName === null) {
$indexName = $this->_generateIdentifierName(
array_merge([$this->getName()], $columnNames),
'uniq',
$this->_getMaxIdentifierLength()
);
}
return $this->_addUniqueConstraint($this->_createUniqueConstraint($columnNames, $indexName, $options));
}
/**
* @param mixed[][] $columnNames
* @param string|null $indexName
......@@ -161,9 +186,11 @@ class Table extends AbstractAsset
public function dropIndex($indexName)
{
$indexName = $this->normalizeIdentifier($indexName);
if (! $this->hasIndex($indexName)) {
throw SchemaException::indexDoesNotExist($indexName, $this->_name);
}
unset($this->_indexes[$indexName]);
}
......@@ -252,37 +279,6 @@ class Table extends AbstractAsset
return false;
}
/**
* @param mixed[][] $columnNames
* @param string $indexName
* @param bool $isUnique
* @param bool $isPrimary
* @param string[] $flags
* @param mixed[] $options
*
* @return Index
*
* @throws SchemaException
*/
private function _createIndex(array $columnNames, $indexName, $isUnique, $isPrimary, array $flags = [], array $options = [])
{
if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) {
throw SchemaException::indexNameInvalid($indexName);
}
foreach ($columnNames as $columnName => $indexColOptions) {
if (is_numeric($columnName) && is_string($indexColOptions)) {
$columnName = $indexColOptions;
}
if (! $this->hasColumn($columnName)) {
throw SchemaException::columnDoesNotExist($columnName, $this->_name);
}
}
return new Index($indexName, $columnNames, $isUnique, $isPrimary, $flags, $options);
}
/**
* @param string $columnName
* @param string $typeName
......@@ -311,9 +307,11 @@ class Table extends AbstractAsset
*/
public function renameColumn($oldColumnName, $newColumnName)
{
throw new DBALException('Table#renameColumn() was removed, because it drops and recreates ' .
'the column instead. There is no fix available, because a schema diff cannot reliably detect if a ' .
'column was renamed or one column was created and another one dropped.');
throw new DBALException(
'Table#renameColumn() was removed, because it drops and recreates the column instead. ' .
'There is no fix available, because a schema diff cannot reliably detect if a column ' .
'was renamed or one column was created and another one dropped.'
);
}
/**
......@@ -327,6 +325,7 @@ class Table extends AbstractAsset
public function changeColumn($columnName, array $options)
{
$column = $this->getColumn($columnName);
$column->setOptions($options);
return $this;
......@@ -342,6 +341,7 @@ class Table extends AbstractAsset
public function dropColumn($columnName)
{
$columnName = $this->normalizeIdentifier($columnName);
unset($this->_columns[$columnName]);
return $this;
......@@ -362,7 +362,13 @@ class Table extends AbstractAsset
*/
public function addForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [], $constraintName = null)
{
$constraintName = $constraintName ?: $this->_generateIdentifierName(array_merge((array) $this->getName(), $localColumnNames), 'fk', $this->_getMaxIdentifierLength());
if (! $constraintName) {
$constraintName = $this->_generateIdentifierName(
array_merge((array) $this->getName(), $localColumnNames),
'fk',
$this->_getMaxIdentifierLength()
);
}
return $this->addNamedForeignKeyConstraint($constraintName, $foreignTable, $localColumnNames, $foreignColumnNames, $options);
}
......@@ -424,9 +430,8 @@ class Table extends AbstractAsset
$name,
$options
);
$this->_addForeignKeyConstraint($constraint);
return $this;
return $this->_addForeignKeyConstraint($constraint);
}
/**
......@@ -443,137 +448,95 @@ class Table extends AbstractAsset
}
/**
* @return void
* Returns whether this table has a foreign key constraint with the given name.
*
* @throws SchemaException
* @param string $constraintName
*
* @return bool
*/
protected function _addColumn(Column $column)
public function hasForeignKey($constraintName)
{
$columnName = $column->getName();
$columnName = $this->normalizeIdentifier($columnName);
if (isset($this->_columns[$columnName])) {
throw SchemaException::columnAlreadyExists($this->getName(), $columnName);
}
$constraintName = $this->normalizeIdentifier($constraintName);
$this->_columns[$columnName] = $column;
return isset($this->_fkConstraints[$constraintName]);
}
/**
* Adds an index to the table.
* Returns the foreign key constraint with the given name.
*
* @return self
* @param string $constraintName The constraint name.
*
* @throws SchemaException
* @return ForeignKeyConstraint
*
* @throws SchemaException If the foreign key does not exist.
*/
protected function _addIndex(Index $indexCandidate)
public function getForeignKey($constraintName)
{
$indexName = $indexCandidate->getName();
$indexName = $this->normalizeIdentifier($indexName);
$replacedImplicitIndexes = [];
foreach ($this->implicitIndexes as $name => $implicitIndex) {
if (! $implicitIndex->isFullfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) {
continue;
}
$replacedImplicitIndexes[] = $name;
}
if ((isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) ||
($this->_primaryKeyName !== false && $indexCandidate->isPrimary())
) {
throw SchemaException::indexAlreadyExists($indexName, $this->_name);
}
foreach ($replacedImplicitIndexes as $name) {
unset($this->_indexes[$name], $this->implicitIndexes[$name]);
}
$constraintName = $this->normalizeIdentifier($constraintName);
if ($indexCandidate->isPrimary()) {
$this->_primaryKeyName = $indexName;
if (! $this->hasForeignKey($constraintName)) {
throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
}
$this->_indexes[$indexName] = $indexCandidate;
return $this;
return $this->_fkConstraints[$constraintName];
}
/**
* Removes the foreign key constraint with the given name.
*
* @param string $constraintName The constraint name.
*
* @return void
*
* @throws SchemaException
*/
protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint)
public function removeForeignKey($constraintName)
{
$constraint->setLocalTable($this);
if (strlen($constraint->getName())) {
$name = $constraint->getName();
} else {
$name = $this->_generateIdentifierName(
array_merge((array) $this->getName(), $constraint->getLocalColumns()),
'fk',
$this->_getMaxIdentifierLength()
);
}
$name = $this->normalizeIdentifier($name);
$this->_fkConstraints[$name] = $constraint;
// add an explicit index on the foreign key columns. If there is already an index that fulfils 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 (based on columns).
$indexName = $this->_generateIdentifierName(
array_merge([$this->getName()], $constraint->getColumns()),
'idx',
$this->_getMaxIdentifierLength()
);
$indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false);
$constraintName = $this->normalizeIdentifier($constraintName);
foreach ($this->_indexes as $existingIndex) {
if ($indexCandidate->isFullfilledBy($existingIndex)) {
return;
}
if (! $this->hasForeignKey($constraintName)) {
throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
}
$this->_addIndex($indexCandidate);
$this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate;
unset($this->_fkConstraints[$constraintName]);
}
/**
* Returns whether this table has a foreign key constraint with the given name.
* Returns whether this table has a unique constraint with the given name.
*
* @param string $constraintName
*
* @return bool
*/
public function hasForeignKey($constraintName)
public function hasUniqueConstraint($constraintName)
{
$constraintName = $this->normalizeIdentifier($constraintName);
return isset($this->_fkConstraints[$constraintName]);
return isset($this->_uniqueConstraints[$constraintName]);
}
/**
* Returns the foreign key constraint with the given name.
* Returns the unique constraint with the given name.
*
* @param string $constraintName The constraint name.
*
* @return ForeignKeyConstraint
* @return UniqueConstraint
*
* @throws SchemaException If the foreign key does not exist.
*/
public function getForeignKey($constraintName)
public function getUniqueConstraint($constraintName)
{
$constraintName = $this->normalizeIdentifier($constraintName);
if (! $this->hasForeignKey($constraintName)) {
throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
if (! $this->hasUniqueConstraint($constraintName)) {
throw SchemaException::uniqueConstraintDoesNotExist($constraintName, $this->_name);
}
return $this->_fkConstraints[$constraintName];
return $this->_uniqueConstraints[$constraintName];
}
/**
* Removes the foreign key constraint with the given name.
* Removes the unique constraint with the given name.
*
* @param string $constraintName The constraint name.
*
......@@ -581,14 +544,15 @@ class Table extends AbstractAsset
*
* @throws SchemaException
*/
public function removeForeignKey($constraintName)
public function removeUniqueConstraint($constraintName)
{
$constraintName = $this->normalizeIdentifier($constraintName);
if (! $this->hasForeignKey($constraintName)) {
throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
if (! $this->hasUniqueConstraint($constraintName)) {
throw SchemaException::uniqueConstraintDoesNotExist($constraintName, $this->_name);
}
unset($this->_fkConstraints[$constraintName]);
unset($this->_uniqueConstraints[$constraintName]);
}
/**
......@@ -598,27 +562,19 @@ class Table extends AbstractAsset
*/
public function getColumns()
{
$primaryKeyColumns = [];
$pkCols = [];
$fkCols = [];
if ($this->hasPrimaryKey()) {
$primaryKeyColumns = $this->filterColumns($this->getPrimaryKey()->getColumns());
$pkCols = $this->filterColumns($this->getPrimaryKey()->getColumns());
}
return array_merge($primaryKeyColumns, $this->getForeignKeyColumns(), $this->_columns);
foreach ($this->getForeignKeys() as $fk) {
/** @var ForeignKeyConstraint $fk */
$fkCols = array_merge($fkCols, $fk->getColumns());
}
/**
* Returns foreign key columns
*
* @return Column[]
*/
private function getForeignKeyColumns()
{
$foreignKeyColumns = [];
foreach ($this->getForeignKeys() as $foreignKey) {
/** @var ForeignKeyConstraint $foreignKey */
$foreignKeyColumns = array_merge($foreignKeyColumns, $foreignKey->getColumns());
}
return $this->filterColumns($foreignKeyColumns);
return array_unique(array_merge($pkCols, $fkCols, array_keys($this->_columns)));
}
/**
......@@ -661,6 +617,7 @@ class Table extends AbstractAsset
public function getColumn($columnName)
{
$columnName = $this->normalizeIdentifier($columnName);
if (! $this->hasColumn($columnName)) {
throw SchemaException::columnDoesNotExist($columnName, $this->_name);
}
......@@ -675,11 +632,9 @@ class Table extends AbstractAsset
*/
public function getPrimaryKey()
{
if (! $this->hasPrimaryKey()) {
return null;
}
return $this->getIndex($this->_primaryKeyName);
return $this->hasPrimaryKey()
? $this->getIndex($this->_primaryKeyName)
: null;
}
/**
......@@ -733,6 +688,7 @@ class Table extends AbstractAsset
public function getIndex($indexName)
{
$indexName = $this->normalizeIdentifier($indexName);
if (! $this->hasIndex($indexName)) {
throw SchemaException::indexDoesNotExist($indexName, $this->_name);
}
......@@ -748,6 +704,16 @@ class Table extends AbstractAsset
return $this->_indexes;
}
/**
* Returns the unique constraints.
*
* @return UniqueConstraint[]
*/
public function getUniqueConstraints()
{
return $this->_uniqueConstraints;
}
/**
* Returns the foreign key constraints.
*
......@@ -816,15 +782,166 @@ class Table extends AbstractAsset
foreach ($this->_columns as $k => $column) {
$this->_columns[$k] = clone $column;
}
foreach ($this->_indexes as $k => $index) {
$this->_indexes[$k] = clone $index;
}
foreach ($this->_fkConstraints as $k => $fk) {
$this->_fkConstraints[$k] = clone $fk;
$this->_fkConstraints[$k]->setLocalTable($this);
}
}
/**
* @return int
*/
protected function _getMaxIdentifierLength()
{
return $this->_schemaConfig instanceof SchemaConfig
? $this->_schemaConfig->getMaxIdentifierLength()
: 63;
}
/**
* @return void
*
* @throws SchemaException
*/
protected function _addColumn(Column $column)
{
$columnName = $column->getName();
$columnName = $this->normalizeIdentifier($columnName);
if (isset($this->_columns[$columnName])) {
throw SchemaException::columnAlreadyExists($this->getName(), $columnName);
}
$this->_columns[$columnName] = $column;
}
/**
* Adds an index to the table.
*
* @return self
*
* @throws SchemaException
*/
protected function _addIndex(Index $indexCandidate)
{
$indexName = $indexCandidate->getName();
$indexName = $this->normalizeIdentifier($indexName);
$replacedImplicitIndexes = [];
foreach ($this->implicitIndexes as $name => $implicitIndex) {
if (! $implicitIndex->isFullfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) {
continue;
}
$replacedImplicitIndexes[] = $name;
}
if ((isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) ||
($this->_primaryKeyName !== false && $indexCandidate->isPrimary())
) {
throw SchemaException::indexAlreadyExists($indexName, $this->_name);
}
foreach ($replacedImplicitIndexes as $name) {
unset($this->_indexes[$name], $this->implicitIndexes[$name]);
}
if ($indexCandidate->isPrimary()) {
$this->_primaryKeyName = $indexName;
}
$this->_indexes[$indexName] = $indexCandidate;
return $this;
}
/**
* @return self
*/
protected function _addUniqueConstraint(UniqueConstraint $uniqueConstraint)
{
$name = strlen($uniqueConstraint->getName())
? $uniqueConstraint->getName()
: $this->_generateIdentifierName(
array_merge((array) $this->getName(), $uniqueConstraint->getLocalColumns()),
'fk',
$this->_getMaxIdentifierLength()
);
$name = $this->normalizeIdentifier($name);
$this->_uniqueConstraints[$name] = $uniqueConstraint;
// 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()], $uniqueConstraint->getColumns()),
'idx',
$this->_getMaxIdentifierLength()
);
$indexCandidate = $this->_createIndex($uniqueConstraint->getColumns(), $indexName, true, false);
foreach ($this->_indexes as $existingIndex) {
if ($indexCandidate->isFullfilledBy($existingIndex)) {
return $this;
}
}
$this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate;
return $this;
}
/**
* @return self
*/
protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint)
{
$constraint->setLocalTable($this);
$name = strlen($constraint->getName())
? $constraint->getName()
: $this->_generateIdentifierName(
array_merge((array) $this->getName(), $constraint->getLocalColumns()),
'fk',
$this->_getMaxIdentifierLength()
);
$name = $this->normalizeIdentifier($name);
$this->_fkConstraints[$name] = $constraint;
// add an explicit index on the foreign key columns.
// 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->_getMaxIdentifierLength()
);
$indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false);
foreach ($this->_indexes as $existingIndex) {
if ($indexCandidate->isFullfilledBy($existingIndex)) {
return $this;
}
}
$this->_addIndex($indexCandidate);
$this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate;
return $this;
}
/**
* Normalizes a given identifier.
*
......@@ -838,4 +955,63 @@ class Table extends AbstractAsset
{
return $this->trimQuotes(strtolower($identifier));
}
/**
* @param mixed[] $columnNames
* @param string $indexName
* @param mixed[] $options
*
* @return UniqueConstraint
*
* @throws SchemaException
*/
private function _createUniqueConstraint(array $columnNames, $indexName, array $options = [])
{
if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) {
throw SchemaException::indexNameInvalid($indexName);
}
foreach ($columnNames as $columnName => $indexColOptions) {
if (is_numeric($columnName) && is_string($indexColOptions)) {
$columnName = $indexColOptions;
}
if (! $this->hasColumn($columnName)) {
throw SchemaException::columnDoesNotExist($columnName, $this->_name);
}
}
return new UniqueConstraint($indexName, $columnNames, $options);
}
/**
* @param mixed[] $columnNames
* @param string $indexName
* @param bool $isUnique
* @param bool $isPrimary
* @param string[] $flags
* @param mixed[] $options
*
* @return Index
*
* @throws SchemaException
*/
private function _createIndex(array $columnNames, $indexName, $isUnique, $isPrimary, array $flags = [], array $options = [])
{
if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) {
throw SchemaException::indexNameInvalid($indexName);
}
foreach ($columnNames as $columnName => $indexColOptions) {
if (is_numeric($columnName) && is_string($indexColOptions)) {
$columnName = $indexColOptions;
}
if (! $this->hasColumn($columnName)) {
throw SchemaException::columnDoesNotExist($columnName, $this->_name);
}
}
return new Index($indexName, $columnNames, $isUnique, $isPrimary, $flags, $options);
}
}
<?php
namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use InvalidArgumentException;
use function array_keys;
use function array_map;
use function is_string;
use function strtolower;
/**
* Class for a unique constraint.
*/
class UniqueConstraint extends AbstractAsset implements Constraint
{
/**
* Asset identifier instances of the column names the unique constraint is associated with.
* array($columnName => Identifier)
*
* @var Identifier[]
*/
protected $columns = [];
/**
* Platform specific options
*
* @var mixed[]
*/
private $options = [];
/**
* @param string $indexName
* @param string[] $columns
* @param mixed[] $options
*/
public function __construct($indexName, array $columns, array $options = [])
{
$this->_setName($indexName);
$this->options = $options;
foreach ($columns as $column) {
$this->_addColumn($column);
}
}
/**
* @param string $column
*
* @return void
*
* @throws InvalidArgumentException
*/
protected function _addColumn($column)
{
if (! is_string($column)) {
throw new InvalidArgumentException('Expecting a string as Index Column');
}
$this->_columns[$column] = new Identifier($column);
}
/**
* {@inheritdoc}
*/
public function getColumns()
{
return array_keys($this->_columns);
}
/**
* {@inheritdoc}
*/
public function getQuotedColumns(AbstractPlatform $platform)
{
$columns = [];
foreach ($this->_columns as $column) {
$columns[] = $column->getQuotedName($platform);
}
return $columns;
}
/**
* @return string[]
*/
public function getUnquotedColumns()
{
return array_map([$this, 'trimQuotes'], $this->getColumns());
}
/**
* @param string $name
*
* @return bool
*/
public function hasOption($name)
{
return isset($this->options[strtolower($name)]);
}
/**
* @param string $name
*
* @return mixed
*/
public function getOption($name)
{
return $this->options[strtolower($name)];
}
/**
* @return mixed[]
*/
public function getOptions()
{
return $this->options;
}
}
......@@ -314,6 +314,7 @@ class ExceptionTest extends DbalFunctionalTestCase
$table->addColumn('id', 'integer');
$this->expectException($exceptionClass);
foreach ($schema->toSql($conn->getDatabasePlatform()) as $sql) {
$conn->exec($sql);
}
......
......@@ -976,7 +976,7 @@ class SchemaManagerFunctionalTestCase extends DbalFunctionalTestCase
protected function getTestTable($name, $options = [])
{
$table = new Table($name, [], [], [], false, $options);
$table = new Table($name, [], [], [], [], false, $options);
$table->setSchemaConfig($this->schemaManager->createSchemaConfig());
$table->addColumn('id', 'integer', ['notnull' => true]);
$table->setPrimaryKey(['id']);
......@@ -987,7 +987,7 @@ class SchemaManagerFunctionalTestCase extends DbalFunctionalTestCase
protected function getTestCompositeTable($name)
{
$table = new Table($name, [], [], [], false, []);
$table = new Table($name, [], [], [], [], false, []);
$table->setSchemaConfig($this->schemaManager->createSchemaConfig());
$table->addColumn('id', 'integer', ['notnull' => true]);
$table->addColumn('other_id', 'integer', ['notnull' => true]);
......@@ -999,14 +999,17 @@ class SchemaManagerFunctionalTestCase extends DbalFunctionalTestCase
protected function assertHasTable($tables, $tableName)
{
$foundTable = false;
foreach ($tables as $table) {
self::assertInstanceOf(Table::class, $table, 'No Table instance was found in tables array.');
if (strtolower($table->getName()) !== 'list_tables_test_new_name') {
continue;
}
$foundTable = true;
}
self::assertTrue($foundTable, 'Could not find new table');
}
......
......@@ -1272,6 +1272,7 @@ class ComparatorTest extends TestCase
'id_table1' => new Column('id_table1', Type::getType('integer')),
],
[],
[],
[
new ForeignKeyConstraint(['id_table1'], 'table1', ['id'], 'fk_table2_table1'),
]
......@@ -1285,6 +1286,7 @@ class ComparatorTest extends TestCase
'id_table3' => new Column('id_table3', Type::getType('integer')),
],
[],
[],
[
new ForeignKeyConstraint(['id_table3'], 'table3', ['id'], 'fk_table2_table3'),
]
......
......@@ -200,7 +200,7 @@ class TableTest extends DbalTestCase
{
$constraint = new ForeignKeyConstraint([], 'foo', []);
$tableA = new Table('foo', [], [], [$constraint]);
$tableA = new Table('foo', [], [], [], [$constraint]);
$constraints = $tableA->getForeignKeys();
self::assertCount(1, $constraints);
......@@ -209,7 +209,7 @@ class TableTest extends DbalTestCase
public function testOptions()
{
$table = new Table('foo', [], [], [], false, ['foo' => 'bar']);
$table = new Table('foo', [], [], [], [], false, ['foo' => 'bar']);
self::assertTrue($table->hasOption('foo'));
self::assertEquals('bar', $table->getOption('foo'));
......
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