From bf6655a534a6775d30cafa67bd801276bda1d98d Mon Sep 17 00:00:00 2001 From: polo Date: Tue, 13 Aug 2024 23:45:21 +0200 Subject: =?UTF-8?q?VERSION=200.2=20doctrine=20ORM=20et=20entit=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vendor/doctrine/dbal/src/Schema/Comparator.php | 417 +++++++++++++++++++++++++ 1 file changed, 417 insertions(+) create mode 100644 vendor/doctrine/dbal/src/Schema/Comparator.php (limited to 'vendor/doctrine/dbal/src/Schema/Comparator.php') diff --git a/vendor/doctrine/dbal/src/Schema/Comparator.php b/vendor/doctrine/dbal/src/Schema/Comparator.php new file mode 100644 index 0000000..4c60c07 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/Comparator.php @@ -0,0 +1,417 @@ +getNamespaces() as $newNamespace) { + if ($oldSchema->hasNamespace($newNamespace)) { + continue; + } + + $createdSchemas[] = $newNamespace; + } + + foreach ($oldSchema->getNamespaces() as $oldNamespace) { + if ($newSchema->hasNamespace($oldNamespace)) { + continue; + } + + $droppedSchemas[] = $oldNamespace; + } + + foreach ($newSchema->getTables() as $newTable) { + $newTableName = $newTable->getShortestName($newSchema->getName()); + if (! $oldSchema->hasTable($newTableName)) { + $createdTables[] = $newSchema->getTable($newTableName); + } else { + $tableDiff = $this->compareTables( + $oldSchema->getTable($newTableName), + $newSchema->getTable($newTableName), + ); + + if (! $tableDiff->isEmpty()) { + $alteredTables[] = $tableDiff; + } + } + } + + // Check if there are tables removed + foreach ($oldSchema->getTables() as $oldTable) { + $oldTableName = $oldTable->getShortestName($oldSchema->getName()); + + $oldTable = $oldSchema->getTable($oldTableName); + if ($newSchema->hasTable($oldTableName)) { + continue; + } + + $droppedTables[] = $oldTable; + } + + foreach ($newSchema->getSequences() as $newSequence) { + $newSequenceName = $newSequence->getShortestName($newSchema->getName()); + if (! $oldSchema->hasSequence($newSequenceName)) { + if (! $this->isAutoIncrementSequenceInSchema($oldSchema, $newSequence)) { + $createdSequences[] = $newSequence; + } + } else { + if ($this->diffSequence($newSequence, $oldSchema->getSequence($newSequenceName))) { + $alteredSequences[] = $newSchema->getSequence($newSequenceName); + } + } + } + + foreach ($oldSchema->getSequences() as $oldSequence) { + if ($this->isAutoIncrementSequenceInSchema($newSchema, $oldSequence)) { + continue; + } + + $oldSequenceName = $oldSequence->getShortestName($oldSchema->getName()); + + if ($newSchema->hasSequence($oldSequenceName)) { + continue; + } + + $droppedSequences[] = $oldSequence; + } + + return new SchemaDiff( + $createdSchemas, + $droppedSchemas, + $createdTables, + $alteredTables, + $droppedTables, + $createdSequences, + $alteredSequences, + $droppedSequences, + ); + } + + private function isAutoIncrementSequenceInSchema(Schema $schema, Sequence $sequence): bool + { + foreach ($schema->getTables() as $table) { + if ($sequence->isAutoIncrementsFor($table)) { + return true; + } + } + + return false; + } + + public function diffSequence(Sequence $sequence1, Sequence $sequence2): bool + { + if ($sequence1->getAllocationSize() !== $sequence2->getAllocationSize()) { + return true; + } + + return $sequence1->getInitialValue() !== $sequence2->getInitialValue(); + } + + /** + * Compares the tables and returns the difference between them. + */ + public function compareTables(Table $oldTable, Table $newTable): TableDiff + { + $addedColumns = []; + $modifiedColumns = []; + $droppedColumns = []; + $addedIndexes = []; + $modifiedIndexes = []; + $droppedIndexes = []; + $addedForeignKeys = []; + $modifiedForeignKeys = []; + $droppedForeignKeys = []; + + $oldColumns = $oldTable->getColumns(); + $newColumns = $newTable->getColumns(); + + // See if all the columns in the old table exist in the new table + foreach ($newColumns as $newColumn) { + $newColumnName = strtolower($newColumn->getName()); + + if ($oldTable->hasColumn($newColumnName)) { + continue; + } + + $addedColumns[$newColumnName] = $newColumn; + } + + // See if there are any removed columns in the new table + foreach ($oldColumns as $oldColumn) { + $oldColumnName = strtolower($oldColumn->getName()); + + // See if column is removed in the new table. + if (! $newTable->hasColumn($oldColumnName)) { + $droppedColumns[$oldColumnName] = $oldColumn; + + continue; + } + + $newColumn = $newTable->getColumn($oldColumnName); + + if ($this->columnsEqual($oldColumn, $newColumn)) { + continue; + } + + $modifiedColumns[] = new ColumnDiff($oldColumn, $newColumn); + } + + $renamedColumns = $this->detectRenamedColumns($addedColumns, $droppedColumns); + + $oldIndexes = $oldTable->getIndexes(); + $newIndexes = $newTable->getIndexes(); + + // See if all the indexes from the old table exist in the new one + foreach ($newIndexes as $newIndexName => $newIndex) { + if (($newIndex->isPrimary() && $oldTable->getPrimaryKey() !== null) || $oldTable->hasIndex($newIndexName)) { + continue; + } + + $addedIndexes[$newIndexName] = $newIndex; + } + + // See if there are any removed indexes in the new table + foreach ($oldIndexes as $oldIndexName => $oldIndex) { + // See if the index is removed in the new table. + if ( + ($oldIndex->isPrimary() && $newTable->getPrimaryKey() === null) || + ! $oldIndex->isPrimary() && ! $newTable->hasIndex($oldIndexName) + ) { + $droppedIndexes[$oldIndexName] = $oldIndex; + + continue; + } + + // See if index has changed in the new table. + $newIndex = $oldIndex->isPrimary() ? $newTable->getPrimaryKey() : $newTable->getIndex($oldIndexName); + assert($newIndex instanceof Index); + + if (! $this->diffIndex($oldIndex, $newIndex)) { + continue; + } + + $modifiedIndexes[] = $newIndex; + } + + $renamedIndexes = $this->detectRenamedIndexes($addedIndexes, $droppedIndexes); + + $oldForeignKeys = $oldTable->getForeignKeys(); + $newForeignKeys = $newTable->getForeignKeys(); + + foreach ($oldForeignKeys as $oldKey => $oldForeignKey) { + foreach ($newForeignKeys as $newKey => $newForeignKey) { + if ($this->diffForeignKey($oldForeignKey, $newForeignKey) === false) { + unset($oldForeignKeys[$oldKey], $newForeignKeys[$newKey]); + } else { + if (strtolower($oldForeignKey->getName()) === strtolower($newForeignKey->getName())) { + $modifiedForeignKeys[] = $newForeignKey; + + unset($oldForeignKeys[$oldKey], $newForeignKeys[$newKey]); + } + } + } + } + + foreach ($oldForeignKeys as $oldForeignKey) { + $droppedForeignKeys[] = $oldForeignKey; + } + + foreach ($newForeignKeys as $newForeignKey) { + $addedForeignKeys[] = $newForeignKey; + } + + return new TableDiff( + $oldTable, + $addedColumns, + $modifiedColumns, + $droppedColumns, + $renamedColumns, + $addedIndexes, + $modifiedIndexes, + $droppedIndexes, + $renamedIndexes, + $addedForeignKeys, + $modifiedForeignKeys, + $droppedForeignKeys, + ); + } + + /** + * Try to find columns that only changed their name, rename operations maybe cheaper than add/drop + * however ambiguities between different possibilities should not lead to renaming at all. + * + * @param array $addedColumns + * @param array $removedColumns + * + * @return array + */ + private function detectRenamedColumns(array &$addedColumns, array &$removedColumns): array + { + $candidatesByName = []; + + foreach ($addedColumns as $addedColumnName => $addedColumn) { + foreach ($removedColumns as $removedColumn) { + if (! $this->columnsEqual($addedColumn, $removedColumn)) { + continue; + } + + $candidatesByName[$addedColumn->getName()][] = [$removedColumn, $addedColumn, $addedColumnName]; + } + } + + $renamedColumns = []; + + foreach ($candidatesByName as $candidates) { + if (count($candidates) !== 1) { + continue; + } + + [$removedColumn, $addedColumn] = $candidates[0]; + $removedColumnName = $removedColumn->getName(); + $addedColumnName = strtolower($addedColumn->getName()); + + if (isset($renamedColumns[$removedColumnName])) { + continue; + } + + $renamedColumns[$removedColumnName] = $addedColumn; + unset( + $addedColumns[$addedColumnName], + $removedColumns[strtolower($removedColumnName)], + ); + } + + return $renamedColumns; + } + + /** + * Try to find indexes that only changed their name, rename operations maybe cheaper than add/drop + * however ambiguities between different possibilities should not lead to renaming at all. + * + * @param array $addedIndexes + * @param array $removedIndexes + * + * @return array + */ + private function detectRenamedIndexes(array &$addedIndexes, array &$removedIndexes): array + { + $candidatesByName = []; + + // Gather possible rename candidates by comparing each added and removed index based on semantics. + foreach ($addedIndexes as $addedIndexName => $addedIndex) { + foreach ($removedIndexes as $removedIndex) { + if ($this->diffIndex($addedIndex, $removedIndex)) { + continue; + } + + $candidatesByName[$addedIndex->getName()][] = [$removedIndex, $addedIndex, $addedIndexName]; + } + } + + $renamedIndexes = []; + + foreach ($candidatesByName as $candidates) { + // If the current rename candidate contains exactly one semantically equal index, + // we can safely rename it. + // Otherwise, it is unclear if a rename action is really intended, + // therefore we let those ambiguous indexes be added/dropped. + if (count($candidates) !== 1) { + continue; + } + + [$removedIndex, $addedIndex] = $candidates[0]; + + $removedIndexName = strtolower($removedIndex->getName()); + $addedIndexName = strtolower($addedIndex->getName()); + + if (isset($renamedIndexes[$removedIndexName])) { + continue; + } + + $renamedIndexes[$removedIndexName] = $addedIndex; + unset( + $addedIndexes[$addedIndexName], + $removedIndexes[$removedIndexName], + ); + } + + return $renamedIndexes; + } + + protected function diffForeignKey(ForeignKeyConstraint $key1, ForeignKeyConstraint $key2): bool + { + if ( + array_map('strtolower', $key1->getUnquotedLocalColumns()) + !== array_map('strtolower', $key2->getUnquotedLocalColumns()) + ) { + return true; + } + + if ( + array_map('strtolower', $key1->getUnquotedForeignColumns()) + !== array_map('strtolower', $key2->getUnquotedForeignColumns()) + ) { + return true; + } + + if ($key1->getUnqualifiedForeignTableName() !== $key2->getUnqualifiedForeignTableName()) { + return true; + } + + if ($key1->onUpdate() !== $key2->onUpdate()) { + return true; + } + + return $key1->onDelete() !== $key2->onDelete(); + } + + /** + * Compares the definitions of the given columns + */ + protected function columnsEqual(Column $column1, Column $column2): bool + { + return $this->platform->columnsEqual($column1, $column2); + } + + /** + * Finds the difference between the indexes $index1 and $index2. + * + * Compares $index1 with $index2 and returns true if there are any + * differences or false in case there are no differences. + */ + protected function diffIndex(Index $index1, Index $index2): bool + { + return ! ($index1->isFulfilledBy($index2) && $index2->isFulfilledBy($index1)); + } +} -- cgit v1.2.3