From 44cc6465be89149dabe10fcabe464a0809344bd2 Mon Sep 17 00:00:00 2001 From: beberlei Date: Sat, 28 Nov 2009 01:22:21 +0000 Subject: [PATCH] [2.0] DDC-169 - Savepoint for Refactorings in Schema-Tool - It is now generating DDL according to the old and the new mechanisms in parallel. Equality of generation has been verified on Sqlite, Mysql, Oracle. If Postgres is also verified, the old code will be removed in favour of the new one completly. --- lib/Doctrine/DBAL/Schema/Column.php | 2 +- lib/Doctrine/DBAL/Schema/Schema.php | 4 +- lib/Doctrine/DBAL/Schema/Sequence.php | 4 +- lib/Doctrine/DBAL/Schema/Table.php | 21 ++- .../Visitor/CreateSchemaSqlCollector.php | 29 +--- lib/Doctrine/ORM/Tools/SchemaTool.php | 161 +++++++++++++++--- .../Doctrine/Tests/DBAL/Schema/SchemaTest.php | 6 +- 7 files changed, 171 insertions(+), 56 deletions(-) diff --git a/lib/Doctrine/DBAL/Schema/Column.php b/lib/Doctrine/DBAL/Schema/Column.php index a1d187186..f9919faf9 100644 --- a/lib/Doctrine/DBAL/Schema/Column.php +++ b/lib/Doctrine/DBAL/Schema/Column.php @@ -73,7 +73,7 @@ class Column extends AbstractAsset /** * @var string */ - protected $_default; + protected $_default = null; /** * @var array diff --git a/lib/Doctrine/DBAL/Schema/Schema.php b/lib/Doctrine/DBAL/Schema/Schema.php index 6e772b93e..ef7982f5d 100644 --- a/lib/Doctrine/DBAL/Schema/Schema.php +++ b/lib/Doctrine/DBAL/Schema/Schema.php @@ -198,13 +198,13 @@ class Schema extends AbstractAsset * @param string $sequenceName * @param int $allocationSize * @param int $initialValue - * @return Schema + * @return Sequence */ public function createSequence($sequenceName, $allocationSize=1, $initialValue=1) { $seq = new Sequence($sequenceName, $allocationSize, $initialValue); $this->_addSequence($seq); - return $this; + return $seq; } /** diff --git a/lib/Doctrine/DBAL/Schema/Sequence.php b/lib/Doctrine/DBAL/Schema/Sequence.php index 782ed8c63..369f0e834 100644 --- a/lib/Doctrine/DBAL/Schema/Sequence.php +++ b/lib/Doctrine/DBAL/Schema/Sequence.php @@ -53,8 +53,8 @@ class Sequence extends AbstractAsset public function __construct($name, $allocationSize=1, $initialValue=1) { $this->_setName($name); - $this->_allocationSize = (is_int($allocationSize))?:1; - $this->_initialValue = (is_int($initialValue))?:1; + $this->_allocationSize = (is_numeric($allocationSize))?$allocationSize:1; + $this->_initialValue = (is_numeric($initialValue))?$initialValue:1; } public function getAllocationSize() diff --git a/lib/Doctrine/DBAL/Schema/Table.php b/lib/Doctrine/DBAL/Schema/Table.php index c4d8d8eba..25bf8b483 100644 --- a/lib/Doctrine/DBAL/Schema/Table.php +++ b/lib/Doctrine/DBAL/Schema/Table.php @@ -247,21 +247,28 @@ class Table extends AbstractAsset * @param array $options * @return Table */ - public function addForeignKeyConstraint(Table $foreignTable, array $localColumnNames, array $foreignColumnNames, $name=null, array $options=array()) + public function addForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, $name=null, array $options=array()) { + if ($foreignTable instanceof Table) { + $foreignTableName = $foreignTable->getName(); + + foreach ($foreignColumnNames AS $columnName) { + if (!$foreignTable->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName); + } + } + } else { + $foreignTableName = $foreignTable; + } + foreach ($localColumnNames AS $columnName) { if (!$this->hasColumn($columnName)) { throw SchemaException::columnDoesNotExist($columnName); } } - foreach ($foreignColumnNames AS $columnName) { - if (!$foreignTable->hasColumn($columnName)) { - throw SchemaException::columnDoesNotExist($columnName); - } - } $constraint = new ForeignKeyConstraint( - $localColumnNames, $foreignTable->getName(), $foreignColumnNames, $name, $options + $localColumnNames, $foreignTableName, $foreignColumnNames, $name, $options ); $this->_addConstraint($constraint); return $this; diff --git a/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php b/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php index cf9e4884d..4fee0449e 100644 --- a/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php +++ b/lib/Doctrine/DBAL/Schema/Visitor/CreateSchemaSqlCollector.php @@ -93,36 +93,17 @@ class CreateSchemaSqlCollector implements Visitor } } } -/** - $column = array(); - $column['name'] = $class->getQuotedColumnName($mapping['fieldName'], $this->_platform); - $column['type'] = Type::getType($mapping['type']); - $column['length'] = isset($mapping['length']) ? $mapping['length'] : null; - $column['notnull'] = isset($mapping['nullable']) ? ! $mapping['nullable'] : true; - $column['unique'] = isset($mapping['unique']) ? $mapping['unique'] : false; - $column['version'] = $class->isVersioned && $class->versionField == $mapping['fieldName'] ? true : false; - if(strtolower($column['type']) == 'string' && $column['length'] === null) { - $column['length'] = 255; - } - - if (isset($mapping['precision'])) { - $column['precision'] = $mapping['precision']; - } - - if (isset($mapping['scale'])) { - $column['scale'] = $mapping['scale']; - */ $columns = array(); - foreach($columns AS $column) { + foreach($table->getColumns() AS $column) { /* @var \Doctrine\DBAL\Schema\Column $column */ $columnData = array(); $columnData['name'] = $column->getName(); $columnData['type'] = $column->getType(); $columnData['length'] = $column->getLength(); - $columnData['notnull'] = $column->notNull(); - $columnData['unique'] = false; - $columnData['version'] = ($column->hasPlatformOption("version"))?$column->getPlatformOptions('version'):false; + $columnData['notnull'] = $column->getNotNull(); + $columnData['unique'] = ($column->hasPlatformOption("unique"))?$column->getPlatformOption('unique'):false; + $columnData['version'] = ($column->hasPlatformOption("version"))?$column->getPlatformOption('version'):false; if(strtolower($columnData['type']) == "string" && $columnData['length'] === null) { $columnData['length'] = 255; } @@ -139,7 +120,7 @@ class CreateSchemaSqlCollector implements Visitor } } - $columns[] = $columnData; + $columns[$columnData['name']] = $columnData; } $this->_createTableQueries = array_merge($this->_createTableQueries, diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index 994d1519c..4428d9bbb 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -100,20 +100,30 @@ class SchemaTool $foreignKeyConstraints = array(); // FK SQL statements. Appended to $sql at the end. $sequences = array(); // Sequence SQL statements. Appended to $sql at the end. + $schema = new \Doctrine\DBAL\Schema\Schema(); + foreach ($classes as $class) { if (isset($processedClasses[$class->name]) || $class->isMappedSuperclass) { continue; } + $table = $schema->createTable($class->getQuotedTableName($this->_platform)); + + if ($class->isIdGeneratorIdentity()) { + $table->setIdGeneratorType(\Doctrine\DBAL\Schema\Table::ID_IDENTITY); + } else if ($class->isIdGeneratorSequence()) { + $table->setIdGeneratorType(\Doctrine\DBAL\Schema\Table::ID_SEQUENCE); + } + $options = array(); // table options $columns = array(); // table columns if ($class->isInheritanceTypeSingleTable()) { - $columns = $this->_gatherColumns($class, $options); - $this->_gatherRelationsSql($class, $sql, $columns, $foreignKeyConstraints); + $columns = $this->_gatherColumns($class, $options, $table); + $this->_gatherRelationsSql($class, $sql, $columns, $foreignKeyConstraints, $table, $schema); // Add the discriminator column - $discrColumnDef = $this->_getDiscriminatorColumnDefinition($class); + $discrColumnDef = $this->_getDiscriminatorColumnDefinition($class, $table); $columns[$discrColumnDef['name']] = $discrColumnDef; // Aggregate all the information from all classes in the hierarchy @@ -124,32 +134,41 @@ class SchemaTool foreach ($class->subClasses as $subClassName) { $subClass = $this->_em->getClassMetadata($subClassName); - $columns = array_merge($columns, $this->_gatherColumns($subClass, $options)); - $this->_gatherRelationsSql($subClass, $sql, $columns, $foreignKeyConstraints); + $columns = array_merge($columns, $this->_gatherColumns($subClass, $options, $table)); + $this->_gatherRelationsSql($subClass, $sql, $columns, $foreignKeyConstraints, $table, $schema); $processedClasses[$subClassName] = true; } } else if ($class->isInheritanceTypeJoined()) { // Add all non-inherited fields as columns + $pkColumns = array(); foreach ($class->fieldMappings as $fieldName => $mapping) { if ( ! isset($mapping['inherited'])) { $columnName = $class->getQuotedColumnName($mapping['fieldName'], $this->_platform); - $columns[$columnName] = $this->_gatherColumn($class, $mapping, $options); + $columns[$columnName] = $this->_gatherColumn($class, $mapping, $options, $table); + + if ($class->isIdentifier($fieldName)) { + $pkColumns[] = $columnName; + } } } - $this->_gatherRelationsSql($class, $sql, $columns, $foreignKeyConstraints); + $this->_gatherRelationsSql($class, $sql, $columns, $foreignKeyConstraints, $table, $schema); // Add the discriminator column only to the root table if ($class->name == $class->rootEntityName) { - $discrColumnDef = $this->_getDiscriminatorColumnDefinition($class); + $discrColumnDef = $this->_getDiscriminatorColumnDefinition($class, $table); $columns[$discrColumnDef['name']] = $discrColumnDef; } else { // Add an ID FK column to child tables $idMapping = $class->fieldMappings[$class->identifier[0]]; - $idColumn = $this->_gatherColumn($class, $idMapping, $options); + $idColumn = $this->_gatherColumn($class, $idMapping, $options, $table); unset($idColumn['autoincrement']); - + $pkColumns[] = $idColumn['name']; + if ($table->isIdGeneratorIdentity()) { + $table->setIdGeneratorType(\Doctrine\DBAL\Schema\Table::ID_NONE); + } + $columns[$idColumn['name']] = $idColumn; // Add a FK constraint on the ID column @@ -160,20 +179,37 @@ class SchemaTool $constraint['foreign'] = array($idColumn['name']); $constraint['onDelete'] = 'CASCADE'; $foreignKeyConstraints[] = $constraint; + + $table->addForeignKeyConstraint( + $this->_em->getClassMetadata($class->rootEntityName)->getQuotedTableName($this->_platform), + $constraint['local'], $constraint['foreign'], null, + $constraint + ); } + + $table->setPrimaryKey($pkColumns); + } else if ($class->isInheritanceTypeTablePerClass()) { throw DoctrineException::notSupported(); } else { - $columns = $this->_gatherColumns($class, $options); - $this->_gatherRelationsSql($class, $sql, $columns, $foreignKeyConstraints); + $columns = $this->_gatherColumns($class, $options, $table); + $this->_gatherRelationsSql($class, $sql, $columns, $foreignKeyConstraints, $table, $schema); } if (isset($class->primaryTable['indexes'])) { $options['indexes'] = $class->primaryTable['indexes']; + + foreach($class->primaryTable['indexes'] AS $indexName => $indexData) { + $table->addIndex($indexData, $indexName); + } } if (isset($class->primaryTable['uniqueConstraints'])) { $options['uniqueConstraints'] = $class->primaryTable['uniqueConstraints']; + + foreach($class->primaryTable['uniqueConstraints'] AS $indexName => $indexData) { + $table->addUniqueIndex($indexData, $indexName); + } } $sql = array_merge($sql, $this->_platform->getCreateTableSql( @@ -186,6 +222,15 @@ class SchemaTool // it should not attempt to create a new sequence. if ($class->isIdGeneratorSequence() && $class->name == $class->rootEntityName) { $seqDef = $class->getSequenceGeneratorDefinition(); + + if(!$schema->hasSequence($seqDef['sequenceName'])) { + $schema->createSequence( + $seqDef['sequenceName'], + $seqDef['allocationSize'], + $seqDef['initialValue'] + ); + } + $sequences[] = $this->_platform->getCreateSequenceSql( $seqDef['sequenceName'], $seqDef['initialValue'], @@ -201,8 +246,24 @@ class SchemaTool } } + try { + $newSql = $schema->toSql($this->_platform); + #return $newSql; + } catch(\Exception $e) { + + } + // Append the sequence SQL $sql = array_merge($sql, $sequences); + /*$sql2 = $sql; + sort($sql2); + sort($newSql); + + if($newSql !== $sql2) { + var_dump($newSql); + var_dump($sql2); + die(); + }*/ return $sql; } @@ -215,9 +276,15 @@ class SchemaTool * @return array The portable column definition of the discriminator column as required by * the DBAL. */ - private function _getDiscriminatorColumnDefinition($class) + private function _getDiscriminatorColumnDefinition($class, $table) { $discrColumn = $class->discriminatorColumn; + + $table->createColumn( + $class->getQuotedDiscriminatorColumnName($this->_platform), + $discrColumn['type'], + array('length' => $discrColumn['length'], 'notnull' => true) + ); return array( 'name' => $class->getQuotedDiscriminatorColumnName($this->_platform), @@ -236,14 +303,20 @@ class SchemaTool * that are required by columns should be appended. * @return array The list of portable column definitions as required by the DBAL. */ - private function _gatherColumns($class, array &$options) + private function _gatherColumns($class, array &$options, $table) { $columns = array(); + $pkColumns = array(); foreach ($class->fieldMappings as $fieldName => $mapping) { - $column = $this->_gatherColumn($class, $mapping, $options); + $column = $this->_gatherColumn($class, $mapping, $options, $table); $columns[$column['name']] = $column; + + if ($class->isIdentifier($mapping['fieldName'])) { + $pkColumns[] = $column['name']; + } } + $table->setPrimaryKey($pkColumns); return $columns; } @@ -257,7 +330,7 @@ class SchemaTool * required by the column should be appended. * @return array The portable column definition as required by the DBAL. */ - private function _gatherColumn($class, array $mapping, array &$options) + private function _gatherColumn($class, array $mapping, array &$options, $table) { $column = array(); $column['name'] = $class->getQuotedColumnName($mapping['fieldName'], $this->_platform); @@ -282,6 +355,10 @@ class SchemaTool if (isset($mapping['default'])) { $column['default'] = $mapping['default']; } + + $column['platformOptions']['unique'] = $column['unique']; + $column['platformOptions']['version'] = $column['version']; + $table->createColumn($column['name'], $mapping['type'], $column); if ($class->isIdentifier($mapping['fieldName'])) { $column['primary'] = true; @@ -307,7 +384,7 @@ class SchemaTool * required by relations should be appended. * @return void */ - private function _gatherRelationsSql($class, array &$sql, array &$columns, array &$constraints) + private function _gatherRelationsSql($class, array &$sql, array &$columns, array &$constraints, $table, $schema) { foreach ($class->associationMappings as $fieldName => $mapping) { if (isset($class->inheritedAssociationFields[$fieldName])) { @@ -336,6 +413,8 @@ class SchemaTool } $column['type'] = Type::getType($foreignClass->getTypeOfField($referencedFieldName)); + $table->createColumn($column['name'], $foreignClass->getTypeOfField($referencedFieldName), array('notnull' => false)); + $columns[$column['name']] = $column; $constraint['local'][] = $column['name']; $constraint['foreign'][] = $joinColumn['referencedColumnName']; @@ -348,6 +427,12 @@ class SchemaTool $constraint['onDelete'] = $joinColumn['onDelete']; } } + + $table->addForeignKeyConstraint( + $foreignClass->getQuotedTableName($this->_platform), + $constraint['local'], $constraint['foreign'], null, + $constraint + ); $constraints[] = $constraint; } else if ($mapping->isOneToMany() && $mapping->isOwningSide) { @@ -366,14 +451,25 @@ class SchemaTool 'local' => array(), 'foreign' => array() ); - + + $theJoinTable = $schema->createTable($mapping->getQuotedJoinTableName($this->_platform)); + + $primaryKeyColumns = array(); foreach ($joinTable['joinColumns'] as $joinColumn) { $column = array(); $column['primary'] = true; $joinTableOptions['primary'][] = $joinColumn['name']; + $primaryKeyColumns[] = $joinColumn['name']; $column['name'] = $mapping->getQuotedJoinColumnName($joinColumn['name'], $this->_platform); $column['type'] = Type::getType($class->getTypeOfColumn($joinColumn['referencedColumnName'])); $joinTableColumns[$column['name']] = $column; + + $theJoinTable->createColumn( + $mapping->getQuotedJoinColumnName($joinColumn['name'], $this->_platform), + $class->getTypeOfColumn($joinColumn['referencedColumnName']), + array('notnull' => false) + ); + $constraint1['local'][] = $column['name']; $constraint1['foreign'][] = $joinColumn['referencedColumnName']; @@ -389,6 +485,12 @@ class SchemaTool $constraint1['onDelete'] = $joinColumn['onDelete']; } } + + $theJoinTable->addForeignKeyConstraint( + $class->getQuotedTableName($this->_platform), + $constraint1['local'], $constraint1['foreign'], null, + $constraint1 + ); $constraints[] = $constraint1; @@ -403,6 +505,7 @@ class SchemaTool $column = array(); $column['primary'] = true; $joinTableOptions['primary'][] = $inverseJoinColumn['name']; + $primaryKeyColumns[] = $inverseJoinColumn['name']; $column['name'] = $inverseJoinColumn['name']; $column['type'] = Type::getType($this->_em->getClassMetadata($mapping->getTargetEntityName()) ->getTypeOfColumn($inverseJoinColumn['referencedColumnName'])); @@ -410,6 +513,13 @@ class SchemaTool $constraint2['local'][] = $inverseJoinColumn['name']; $constraint2['foreign'][] = $inverseJoinColumn['referencedColumnName']; + $theJoinTable->createColumn( + $inverseJoinColumn['name'], + $this->_em->getClassMetadata($mapping->getTargetEntityName()) + ->getTypeOfColumn($inverseJoinColumn['referencedColumnName']), + array('notnull' => false) + ); + if(isset($inverseJoinColumn['unique']) && $inverseJoinColumn['unique'] == true) { $joinTableOptions['uniqueConstraints'][] = array($inverseJoinColumn['name']); } @@ -422,6 +532,18 @@ class SchemaTool $constraint2['onDelete'] = $inverseJoinColumn['onDelete']; } } + + foreach($joinTableOptions['uniqueConstraints'] AS $unique) { + $theJoinTable->addUniqueIndex($unique); + } + + $theJoinTable->addForeignKeyConstraint( + $foreignClass->getQuotedTableName($this->_platform), + $constraint2['local'], $constraint2['foreign'], null, + $constraint2 + ); + + $theJoinTable->setPrimaryKey($primaryKeyColumns); $constraints[] = $constraint2; @@ -728,7 +850,8 @@ class SchemaTool foreach ($newFields as $newField) { $options = array(); - $changes['add'][$newField['columnName']] = $this->_gatherColumn($class, $newField, $options); + $table = new \Doctrine\DBAL\Schema\Table("foo"); + $changes['add'][$newField['columnName']] = $this->_gatherColumn($class, $newField, $options, $table); } foreach ($newJoinColumns as $name => $joinColumn) { diff --git a/tests/Doctrine/Tests/DBAL/Schema/SchemaTest.php b/tests/Doctrine/Tests/DBAL/Schema/SchemaTest.php index 913b00250..b6eb865be 100644 --- a/tests/Doctrine/Tests/DBAL/Schema/SchemaTest.php +++ b/tests/Doctrine/Tests/DBAL/Schema/SchemaTest.php @@ -108,7 +108,11 @@ class SchemaTest extends \PHPUnit_Framework_TestCase public function testCreateSequence() { $schema = new Schema(); - $schema->createSequence('a_seq'); + $sequence = $schema->createSequence('a_seq', 10, 20); + + $this->assertEquals('a_seq', $sequence->getName()); + $this->assertEquals(10, $sequence->getAllocationSize()); + $this->assertEquals(20, $sequence->getInitialValue()); $this->assertTrue($schema->hasSequence("a_seq")); $this->assertType('Doctrine\DBAL\Schema\Sequence', $schema->getSequence("a_seq"));