/** * Detects the differences between current connected database and $pDatabase * and updates the schema. This does not DROP tables. * * @param Database $pDatabase */ public function updateSchema($pDatabase) { $diff = DatabaseComparator::computeDiff($this->database, $pDatabase); $sql = $this->database->getPlatform()->getModifyDatabaseDDL($diff); $statements = SqlParser::parseString($sql); foreach ($statements as $statement) { if (strpos($statement, 'DROP') === 0) { // drop statements cause errors since the table doesn't exist continue; } $stmt = $this->con->prepare($statement); $stmt->execute(); } }
public function testTableRenaming() { $schema1 = ' <database name="test"> <table name="foo"> <column name="id" primaryKey="true" type="INTEGER" autoIncrement="true" /> <column name="bar1" type="INTEGER" /> <column name="bar2" type="INTEGER" /> </table> <table name="foo2"> <column name="id" primaryKey="true" type="INTEGER" autoIncrement="true" /> <column name="bar1" type="INTEGER" /> <column name="bar2" type="INTEGER" /> </table> </database> '; $schema2 = ' <database name="test"> <table name="foo_bla"> <column name="id" primaryKey="true" type="INTEGER" autoIncrement="true" /> <column name="bar1" type="INTEGER" /> <column name="bar2" type="INTEGER" /> </table> <table name="foo_bla2"> <column name="id" primaryKey="true" type="INTEGER" autoIncrement="true" /> <column name="bar1" type="INTEGER" /> <column name="bar2" type="INTEGER" /> </table> </database> '; $d1 = $this->getDatabaseFromSchema($schema1); $d2 = $this->getDatabaseFromSchema($schema2); $diff = DatabaseComparator::computeDiff($d1, $d2, false, true); $renamedTables = $diff->getRenamedTables(); $firstPair = [key($renamedTables), current($renamedTables)]; next($renamedTables); $secondPair = [key($renamedTables), current($renamedTables)]; $this->assertEquals('foo', $firstPair[0]); $this->assertEquals('foo_bla', $firstPair[1]); $this->assertEquals('foo2', $secondPair[0]); $this->assertEquals('foo_bla2', $secondPair[1], 'Table `Foo2` should not renamed to `foo_bla` since we have already renamed a table to this name.'); }
public function providerForTestGetModifyTableForeignKeysSkipSql4DDL() { $schema1 = <<<EOF <database name="test" identifierQuoting="true"> <table name="test"> <column name="test" type="INTEGER" primaryKey="true" autoIncrement="true" required="true" /> <column name="ref_test" type="INTEGER"/> <foreign-key foreignTable="test2" onDelete="CASCADE" onUpdate="CASCADE" skipSql="true"> <reference local="ref_test" foreign="test" /> </foreign-key> </table> <table name="test2"> <column name="test" type="integer" primaryKey="true" /> </table> </database> EOF; $schema2 = <<<EOF <database name="test" identifierQuoting="true"> <table name="test"> <column name="test" type="INTEGER" primaryKey="true" autoIncrement="true" required="true" /> <column name="ref_test" type="INTEGER"/> </table> <table name="test2"> <column name="test" type="integer" primaryKey="true" /> </table> </database> EOF; $d1 = $this->getDatabaseFromSchema($schema1); $d2 = $this->getDatabaseFromSchema($schema2); $diff = DatabaseComparator::computeDiff($d2, $d1); return [[$diff]]; }
/** * Compares the current database with $database. * * @param Database $database */ public function compareCurrentDatabase(Database $database) { $this->readDatabase(); $diff = DatabaseComparator::computeDiff($this->database, $database); if (false !== $diff) { $sql = $this->database->getPlatform()->getModifyDatabaseDDL($diff); $this->fail(sprintf("There are unexpected diffs: \n%s\n`%s`\nCurrent Database: \n%s\nTo XML Database: \n%s\n", $diff, $sql, $this->database, $database)); } $this->assertFalse($diff, 'no changes.'); }
public function updateDB(ConnectionInterface $con) { $database = $this->readConnectedDatabase(); $diff = DatabaseComparator::computeDiff($database, $this->database); if (false === $diff) { return null; } $sql = $this->database->getPlatform()->getModifyDatabaseDDL($diff); $statements = SqlParser::parseString($sql); foreach ($statements as $statement) { try { $stmt = $con->prepare($statement); $stmt->execute(); } catch (\Exception $e) { //echo $sql; //uncomment for better debugging throw new BuildException(sprintf("Can not execute SQL: \n%s\nFrom database: \n%s\n\nTo database: \n%s\n\nDiff:\n%s", $statement, $this->database, $database, $diff), null, $e); } } return $database; }
public function testGetModifyDatabaseWithBlockStorageDDL() { $schema1 = <<<EOF <database name="test"> <table name="foo1"> <column name="id" primaryKey="true" type="INTEGER" autoIncrement="true" /> <column name="blooopoo" type="INTEGER" /> </table> <table name="foo2"> <column name="id" primaryKey="true" type="INTEGER" autoIncrement="true" /> <column name="bar" type="INTEGER" /> <column name="baz" type="VARCHAR" size="12" required="true" /> </table> <table name="foo3"> <column name="id" primaryKey="true" type="INTEGER" autoIncrement="true" /> <column name="yipee" type="INTEGER" /> </table> </database> EOF; $schema2 = <<<EOF <database name="test"> <table name="foo2"> <column name="id" primaryKey="true" type="INTEGER" autoIncrement="true" /> <column name="bar1" type="INTEGER" /> <column name="baz" type="VARCHAR" size="12" required="false" /> <column name="baz3" type="CLOB" /> <vendor type="oracle"> <parameter name="PCTFree" value="20"/> <parameter name="InitTrans" value="4"/> <parameter name="MinExtents" value="1"/> <parameter name="MaxExtents" value="99"/> <parameter name="PCTIncrease" value="0"/> <parameter name="Tablespace" value="L_128K"/> <parameter name="PKPCTFree" value="20"/> <parameter name="PKInitTrans" value="4"/> <parameter name="PKMinExtents" value="1"/> <parameter name="PKMaxExtents" value="99"/> <parameter name="PKPCTIncrease" value="0"/> <parameter name="PKTablespace" value="IL_128K"/> </vendor> </table> <table name="foo4"> <column name="id" primaryKey="true" type="INTEGER" autoIncrement="true" /> <column name="yipee" type="INTEGER" /> <vendor type="oracle"> <parameter name="PCTFree" value="20"/> <parameter name="InitTrans" value="4"/> <parameter name="MinExtents" value="1"/> <parameter name="MaxExtents" value="99"/> <parameter name="PCTIncrease" value="0"/> <parameter name="Tablespace" value="L_128K"/> <parameter name="PKPCTFree" value="20"/> <parameter name="PKInitTrans" value="4"/> <parameter name="PKMinExtents" value="1"/> <parameter name="PKMaxExtents" value="99"/> <parameter name="PKPCTIncrease" value="0"/> <parameter name="PKTablespace" value="IL_128K"/> </vendor> </table> <table name="foo5"> <column name="id" primaryKey="true" type="INTEGER" autoIncrement="true" /> <column name="lkdjfsh" type="INTEGER" /> <column name="dfgdsgf" type="CLOB" /> <index name="lkdjfsh_IDX"> <index-column name="lkdjfsh"/> <vendor type="oracle"> <parameter name="PCTFree" value="20"/> <parameter name="InitTrans" value="4"/> <parameter name="MinExtents" value="1"/> <parameter name="MaxExtents" value="99"/> <parameter name="PCTIncrease" value="0"/> <parameter name="Tablespace" value="L_128K"/> </vendor> </index> <vendor type="oracle"> <parameter name="PCTFree" value="20"/> <parameter name="InitTrans" value="4"/> <parameter name="MinExtents" value="1"/> <parameter name="MaxExtents" value="99"/> <parameter name="PCTIncrease" value="0"/> <parameter name="Tablespace" value="L_128K"/> <parameter name="PKPCTFree" value="20"/> <parameter name="PKInitTrans" value="4"/> <parameter name="PKMinExtents" value="1"/> <parameter name="PKMaxExtents" value="99"/> <parameter name="PKPCTIncrease" value="0"/> <parameter name="PKTablespace" value="IL_128K"/> </vendor> </table> </database> EOF; $d1 = $this->getDatabaseFromSchema($schema1); $d2 = $this->getDatabaseFromSchema($schema2); $databaseDiff = DatabaseComparator::computeDiff($d1, $d2); $expected = "\nALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD';\nALTER SESSION SET NLS_TIMESTAMP_FORMAT='YYYY-MM-DD HH24:MI:SS';\n\nDROP TABLE foo1 CASCADE CONSTRAINTS;\n\nDROP SEQUENCE foo1_SEQ;\n\nDROP TABLE foo3 CASCADE CONSTRAINTS;\n\nDROP SEQUENCE foo3_SEQ;\n\nCREATE TABLE foo4\n(\n id NUMBER NOT NULL,\n yipee NUMBER\n)\nPCTFREE 20\nINITRANS 4\nSTORAGE\n(\n MINEXTENTS 1\n MAXEXTENTS 99\n PCTINCREASE 0\n)\nTABLESPACE L_128K;\n\nALTER TABLE foo4 ADD CONSTRAINT foo4_pk PRIMARY KEY (id)\nUSING INDEX\nPCTFREE 20\nINITRANS 4\nSTORAGE\n(\n MINEXTENTS 1\n MAXEXTENTS 99\n PCTINCREASE 0\n)\nTABLESPACE IL_128K;\n\nCREATE SEQUENCE foo4_SEQ\n INCREMENT BY 1 START WITH 1 NOMAXVALUE NOCYCLE NOCACHE ORDER;\n\nCREATE TABLE foo5\n(\n id NUMBER NOT NULL,\n lkdjfsh NUMBER,\n dfgdsgf CLOB\n)\nPCTFREE 20\nINITRANS 4\nSTORAGE\n(\n MINEXTENTS 1\n MAXEXTENTS 99\n PCTINCREASE 0\n)\nTABLESPACE L_128K;\n\nALTER TABLE foo5 ADD CONSTRAINT foo5_pk PRIMARY KEY (id)\nUSING INDEX\nPCTFREE 20\nINITRANS 4\nSTORAGE\n(\n MINEXTENTS 1\n MAXEXTENTS 99\n PCTINCREASE 0\n)\nTABLESPACE IL_128K;\n\nCREATE SEQUENCE foo5_SEQ\n INCREMENT BY 1 START WITH 1 NOMAXVALUE NOCYCLE NOCACHE ORDER;\n\nCREATE INDEX lkdjfsh_IDX ON foo5 (lkdjfsh)\nPCTFREE 20\nINITRANS 4\nSTORAGE\n(\n MINEXTENTS 1\n MAXEXTENTS 99\n PCTINCREASE 0\n)\nTABLESPACE L_128K;\n\nALTER TABLE foo2 RENAME COLUMN bar TO bar1;\n\nALTER TABLE foo2\n\n MODIFY\n(\n baz NVARCHAR2(12)\n),\n\n ADD\n(\n baz3 CLOB\n);\n"; $this->assertEquals($expected, $this->getPlatform()->getModifyDatabaseDDL($databaseDiff)); }
public function testCompareSeveralTableDifferences() { $d1 = new Database(); $t1 = new Table('Foo_Table'); $c1 = new Column('Foo'); $c1->getDomain()->copy($this->platform->getDomainForType('DOUBLE')); $c1->getDomain()->replaceScale(2); $c1->getDomain()->replaceSize(3); $c1->setNotNull(true); $c1->getDomain()->setDefaultValue(new ColumnDefaultValue(123, ColumnDefaultValue::TYPE_VALUE)); $t1->addColumn($c1); $d1->addTable($t1); $t2 = new Table('Bar'); $c2 = new Column('Bar_Column'); $c2->getDomain()->copy($this->platform->getDomainForType('DOUBLE')); $t2->addColumn($c2); $d1->addTable($t2); $t11 = new Table('Baz'); $d1->addTable($t11); $d2 = new Database(); $t3 = new Table('Foo_Table'); $c3 = new Column('Foo1'); $c3->getDomain()->copy($this->platform->getDomainForType('DOUBLE')); $c3->getDomain()->replaceScale(2); $c3->getDomain()->replaceSize(3); $c3->setNotNull(true); $c3->getDomain()->setDefaultValue(new ColumnDefaultValue(123, ColumnDefaultValue::TYPE_VALUE)); $t3->addColumn($c3); $d2->addTable($t3); $t4 = new Table('Bar2'); $c4 = new Column('Bar_Column'); $c4->getDomain()->copy($this->platform->getDomainForType('DOUBLE')); $t4->addColumn($c4); $d2->addTable($t4); $t5 = new Table('Biz'); $c5 = new Column('Biz_Column'); $c5->getDomain()->copy($this->platform->getDomainForType('INTEGER')); $t5->addColumn($c5); $d2->addTable($t5); // Foo_Table was modified, Bar was renamed, Baz was removed, Biz was added $dc = new DatabaseComparator(); $dc->setFromDatabase($d1); $dc->setToDatabase($d2); $nbDiffs = $dc->compareTables(); $databaseDiff = $dc->getDatabaseDiff(); $this->assertEquals(4, $nbDiffs); $this->assertEquals(array('Bar' => 'Bar2'), $databaseDiff->getRenamedTables()); $this->assertEquals(array('Biz' => $t5), $databaseDiff->getAddedTables()); $this->assertEquals(array('Baz' => $t11), $databaseDiff->getRemovedTables()); $tableDiff = TableComparator::computeDiff($t1, $t3); $this->assertEquals(array('Foo_Table' => $tableDiff), $databaseDiff->getModifiedTables()); }
private function checkDeletedFk() { $this->readDatabase(); $diff = DatabaseComparator::computeDiff($this->database, $this->updatedBuilder->getDatabase()); $sql = $this->database->getPlatform()->getModifyDatabaseDDL($diff); $expected = 'issue617_user'; $this->assertNotContains($expected, $sql); }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $configOptions = []; if ($this->hasInputOption('connection', $input)) { foreach ($input->getOption('connection') as $conn) { $configOptions += $this->connectionToProperties($conn); } } if ($this->hasInputOption('migration-table', $input)) { $configOptions['propel']['migrations']['tableName'] = $input->getOption('migration-table'); } if ($this->hasInputOption('schema-dir', $input)) { $configOptions['propel']['paths']['schemaDir'] = $input->getOption('schema-dir'); } if ($this->hasInputOption('output-dir', $input)) { $configOptions['propel']['paths']['migrationDir'] = $input->getOption('output-dir'); } $generatorConfig = $this->getGeneratorConfig($configOptions, $input); $this->createDirectory($generatorConfig->getSection('paths')['migrationDir']); $manager = new MigrationManager(); $manager->setGeneratorConfig($generatorConfig); $manager->setSchemas($this->getSchemas($generatorConfig->getSection('paths')['schemaDir'], $input->getOption('recursive'))); $connections = []; $optionConnections = $input->getOption('connection'); if (!$optionConnections) { $connections = $generatorConfig->getBuildConnections(); } else { foreach ($optionConnections as $connection) { list($name, $dsn, $infos) = $this->parseConnection($connection); $connections[$name] = array_merge(['dsn' => $dsn], $infos); } } $manager->setConnections($connections); $manager->setMigrationTable($generatorConfig->getConfigProperty('migrations.tableName')); $manager->setWorkingDirectory($generatorConfig->getSection('paths')['migrationDir']); if ($manager->hasPendingMigrations()) { throw new RuntimeException('Uncommitted migrations have been found ; you should either execute or delete them before rerunning the \'diff\' task'); } $totalNbTables = 0; $reversedSchema = new Schema(); foreach ($manager->getDatabases() as $appDatabase) { $name = $appDatabase->getName(); if (!($params = @$connections[$name])) { $output->writeln(sprintf('<info>No connection configured for database "%s"</info>', $name)); } if ($input->getOption('verbose')) { $output->writeln(sprintf('Connecting to database "%s" using DSN "%s"', $name, $params['dsn'])); } $conn = $manager->getAdapterConnection($name); $platform = $generatorConfig->getConfiguredPlatform($conn, $name); if (!$platform->supportsMigrations()) { $output->writeln(sprintf('Skipping database "%s" since vendor "%s" does not support migrations', $name, $platform->getDatabaseType())); continue; } $additionalTables = []; foreach ($appDatabase->getTables() as $table) { if ($table->getSchema() && $table->getSchema() != $appDatabase->getSchema()) { $additionalTables[] = $table; } } if ($input->getOption('disable-identifier-quoting')) { $platform->setIdentifierQuoting(false); } $database = new Database($name); $database->setPlatform($platform); $database->setSchema($appDatabase->getSchema()); $database->setDefaultIdMethod(IdMethod::NATIVE); $parser = $generatorConfig->getConfiguredSchemaParser($conn, $name); $nbTables = $parser->parse($database, $additionalTables); $reversedSchema->addDatabase($database); $totalNbTables += $nbTables; if ($input->getOption('verbose')) { $output->writeln(sprintf('%d tables found in database "%s"', $nbTables, $name), Output::VERBOSITY_VERBOSE); } } if ($totalNbTables) { $output->writeln(sprintf('%d tables found in all databases.', $totalNbTables)); } else { $output->writeln('No table found in all databases'); } // comparing models $output->writeln('Comparing models...'); $tableRenaming = $input->getOption('table-renaming'); $migrationsUp = []; $migrationsDown = []; $removeTable = !$input->getOption('skip-removed-table'); $excludedTables = $input->getOption('skip-tables'); foreach ($reversedSchema->getDatabases() as $database) { $name = $database->getName(); if ($input->getOption('verbose')) { $output->writeln(sprintf('Comparing database "%s"', $name)); } if (!($appDataDatabase = $manager->getDatabase($name))) { $output->writeln(sprintf('<error>Database "%s" does not exist in schema.xml. Skipped.</error>', $name)); continue; } $configManager = new ConfigurationManager(); $excludedTables = array_merge((array) $excludedTables, (array) $configManager->getSection('exclude_tables')); $databaseDiff = DatabaseComparator::computeDiff($database, $appDataDatabase, false, $tableRenaming, $removeTable, $excludedTables); if (!$databaseDiff) { if ($input->getOption('verbose')) { $output->writeln(sprintf('Same XML and database structures for datasource "%s" - no diff to generate', $name)); } continue; } $output->writeln(sprintf('Structure of database was modified in datasource "%s": %s', $name, $databaseDiff->getDescription())); foreach ($databaseDiff->getPossibleRenamedTables() as $fromTableName => $toTableName) { $output->writeln(sprintf('<info>Possible table renaming detected: "%s" to "%s". It will be deleted and recreated. Use --table-renaming to only rename it.</info>', $fromTableName, $toTableName)); } $conn = $manager->getAdapterConnection($name); $platform = $generatorConfig->getConfiguredPlatform($conn, $name); if ($input->getOption('disable-identifier-quoting')) { $platform->setIdentifierQuoting(false); } $migrationsUp[$name] = $platform->getModifyDatabaseDDL($databaseDiff); $migrationsDown[$name] = $platform->getModifyDatabaseDDL($databaseDiff->getReverseDiff()); } if (!$migrationsUp) { $output->writeln('Same XML and database structures for all datasource - no diff to generate'); return; } $timestamp = time(); $migrationFileName = $manager->getMigrationFileName($timestamp); $migrationClassBody = $manager->getMigrationClassBody($migrationsUp, $migrationsDown, $timestamp, $input->getOption('comment')); $file = $generatorConfig->getSection('paths')['migrationDir'] . DIRECTORY_SEPARATOR . $migrationFileName; file_put_contents($file, $migrationClassBody); $output->writeln(sprintf('"%s" file successfully created.', $file)); if (null !== ($editorCmd = $input->getOption('editor'))) { $output->writeln(sprintf('Using "%s" as text editor', $editorCmd)); shell_exec($editorCmd . ' ' . escapeshellarg($file)); } else { $output->writeln('Please review the generated SQL statements, and add data migration code if necessary.'); $output->writeln('Once the migration class is valid, call the "migrate" task to execute it.'); } }
/** * Compares the current database with $database. * * @param Database $database * @throws BuildException if a difference has been found between $database and the real database */ public function compareCurrentDatabase(Database $database) { $this->readDatabase(); $diff = DatabaseComparator::computeDiff($this->database, $database); if (false !== $diff) { $sql = $this->database->getPlatform()->getModifyDatabaseDDL($diff); throw new BuildException(sprintf("There are unexpected diffs (real to model): \n%s\n-----%s-----\nCurrent Database: \n%s\nTo XML Database: \n%s\n", $diff, $sql, $this->database, $database)); } $this->assertFalse($diff, 'no changes.'); }
/** * Main method builds all the targets for a typical propel project. */ public function main() { // check to make sure task received all correct params $this->validate(); $generatorConfig = $this->getGeneratorConfig(); // loading model from database $this->log('Reading databases structure...'); $connections = $generatorConfig->getBuildConnections(); if (!$connections) { throw new Exception('You must define database connection settings in a buildtime-conf.xml file to use diff'); } $totalNbTables = 0; $ad = new AppData(); foreach ($connections as $name => $params) { $this->log(sprintf('Connecting to database "%s" using DSN "%s"', $name, $params['dsn']), Project::MSG_VERBOSE); $pdo = $generatorConfig->getBuildPDO($name); $database = new Database($name); $platform = $generatorConfig->getConfiguredPlatform($pdo); if (!$platform->supportsMigrations()) { $this->log(sprintf('Skipping database "%s" since vendor "%s" does not support migrations', $name, $platform->getDatabaseType())); continue; } $database->setPlatform($platform); $database->setDefaultIdMethod(IdMethod::NATIVE); $parser = $generatorConfig->getConfiguredSchemaParser($pdo); $nbTables = $parser->parse($database, $this); $ad->addDatabase($database); $totalNbTables += $nbTables; $this->log(sprintf('%d tables found in database "%s"', $nbTables, $name), Project::MSG_VERBOSE); } if ($totalNbTables) { $this->log(sprintf('%d tables found in all databases.', $totalNbTables)); } else { $this->log('No table found in all databases'); } // loading model from XML $this->packageObjectModel = true; $appDatasFromXml = $this->getDataModels(); $appDataFromXml = array_pop($appDatasFromXml); // comparing models $this->log('Comparing models...'); $manager = new MigrationManager(); $manager->setConnections($connections); $manager->setMigrationDir($this->getOutputDirectory()); $migrationsUp = array(); $migrationsDown = array(); foreach ($ad->getDatabases() as $database) { $name = $database->getName(); $this->log(sprintf('Comparing database "%s"', $name), Project::MSG_VERBOSE); if (!$appDataFromXml->hasDatabase($name)) { // FIXME: tables present in database but not in XML continue; } $databaseDiff = DatabaseComparator::computeDiff($database, $appDataFromXml->getDatabase($name), $this->isCaseInsensitive()); if (!$databaseDiff) { $this->log(sprintf('Same XML and database structures for datasource "%s" - no diff to generate', $name), Project::MSG_VERBOSE); continue; } $this->log(sprintf('Structure of database was modified in datasource "%s": %s', $name, $databaseDiff->getDescription())); $platform = $generatorConfig->getConfiguredPlatform(null, $name); $migrationsUp[$name] = $platform->getModifyDatabaseDDL($databaseDiff); $migrationsDown[$name] = $platform->getModifyDatabaseDDL($databaseDiff->getReverseDiff()); } if (!$migrationsUp) { $this->log('Same XML and database structures for all datasource - no diff to generate'); return; } $timestamp = time(); $migrationFileName = $manager->getMigrationFileName($timestamp); $migrationClassBody = $manager->getMigrationClassBody($migrationsUp, $migrationsDown, $timestamp); $_f = new PhingFile($this->getOutputDirectory(), $migrationFileName); file_put_contents($_f->getAbsolutePath(), $migrationClassBody); $this->log(sprintf('"%s" file successfully created in %s', $_f->getName(), $_f->getParent())); if ($editorCmd = $this->getEditorCmd()) { $this->log(sprintf('Using "%s" as text editor', $editorCmd)); shell_exec($editorCmd . ' ' . escapeshellarg($_f->getAbsolutePath())); } else { $this->log(' Please review the generated SQL statements, and add data migration code if necessary.'); $this->log(' Once the migration class is valid, call the "migrate" task to execute it.'); } }
public function testExcludedTablesWithRenaming() { $dc = new DatabaseComparator(); $this->assertCount(0, $dc->getExcludedTables()); $dc->setExcludedTables(array('foo')); $this->assertCount(1, $dc->getExcludedTables()); $d1 = new Database(); $d2 = new Database(); $t2 = new Table('Bar'); $d2->addTable($t2); $diff = DatabaseComparator::computeDiff($d1, $d2, false, true, true, array('Bar')); $this->assertFalse($diff); $diff = DatabaseComparator::computeDiff($d1, $d2, false, true, true, array('Baz')); $this->assertInstanceOf('Propel\\Generator\\Model\\Diff\\DatabaseDiff', $diff); $d1 = new Database(); $t1 = new Table('Foo'); $d1->addTable($t1); $d2 = new Database(); $t2 = new Table('Bar'); $d2->addTable($t2); $diff = DatabaseComparator::computeDiff($d1, $d2, false, true, true, array('Bar', 'Foo')); $this->assertFalse($diff); $diff = DatabaseComparator::computeDiff($d1, $d2, false, true, true, array('Foo')); $this->assertInstanceOf('Propel\\Generator\\Model\\Diff\\DatabaseDiff', $diff); $diff = DatabaseComparator::computeDiff($d1, $d2, false, true, true, array('Bar')); $this->assertInstanceOf('Propel\\Generator\\Model\\Diff\\DatabaseDiff', $diff); $d1 = new Database(); $t1 = new Table('Foo'); $c1 = new Column('col1'); $t1->addColumn($c1); $d1->addTable($t1); $d2 = new Database(); $t2 = new Table('Foo'); $d2->addTable($t2); $diff = DatabaseComparator::computeDiff($d1, $d2, false, true, true, array('Bar', 'Foo')); $this->assertFalse($diff); $diff = DatabaseComparator::computeDiff($d1, $d2, false, true, true, array('Bar')); $this->assertInstanceOf('Propel\\Generator\\Model\\Diff\\DatabaseDiff', $diff); }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $generatorConfig = $this->getGeneratorConfig(array('propel.platform.class' => $input->getOption('platform'), 'propel.reverse.parser.class' => $this->getReverseClass($input), 'propel.migration.table' => $input->getOption('migration-table')), $input); $this->createDirectory($input->getOption('output-dir')); $manager = new MigrationManager(); $manager->setGeneratorConfig($generatorConfig); $manager->setSchemas($this->getSchemas($input->getOption('input-dir'))); $connections = array(); $optionConnections = $input->getOption('connection'); if (!$optionConnections) { $connections = $generatorConfig->getBuildConnections($input->getOption('input-dir')); } else { foreach ($optionConnections as $connection) { list($name, $dsn, $infos) = $this->parseConnection($connection); $connections[$name] = array_merge(array('dsn' => $dsn), $infos); } } $manager->setConnections($connections); $manager->setMigrationTable($input->getOption('migration-table')); $manager->setWorkingDirectory($input->getOption('output-dir')); if ($manager->hasPendingMigrations()) { throw new RuntimeException('Uncommitted migrations have been found ; you should either execute or delete them before rerunning the \'diff\' task'); } $totalNbTables = 0; $schema = new Schema(); foreach ($connections as $name => $params) { if ($input->getOption('verbose')) { $output->writeln(sprintf('Connecting to database "%s" using DSN "%s"', $name, $params['dsn'])); } $conn = $manager->getAdapterConnection($name); $platform = $generatorConfig->getConfiguredPlatform($conn, $name); if (!$platform->supportsMigrations()) { $output->writeln(sprintf('Skipping database "%s" since vendor "%s" does not support migrations', $name, $platform->getDatabaseType())); continue; } $database = new Database($name); $database->setPlatform($platform); $database->setDefaultIdMethod(IdMethod::NATIVE); $parser = $generatorConfig->getConfiguredSchemaParser($conn); $nbTables = $parser->parse($database, $this); $schema->addDatabase($database); $totalNbTables += $nbTables; if ($input->getOption('verbose')) { $output->writeln(sprintf('%d tables found in database "%s"', $nbTables, $name), Output::VERBOSITY_VERBOSE); } } if ($totalNbTables) { $output->writeln(sprintf('%d tables found in all databases.', $totalNbTables)); } else { $output->writeln('No table found in all databases'); } $appDatasFromXml = $manager->getDataModels(); $appDataFromXml = array_pop($appDatasFromXml); // comparing models $output->writeln('Comparing models...'); $migrationsUp = array(); $migrationsDown = array(); foreach ($schema->getDatabases() as $database) { $name = $database->getName(); if ($input->getOption('verbose')) { $output->writeln(sprintf('Comparing database "%s"', $name)); } if (!$appDataFromXml->hasDatabase($name)) { // FIXME: tables present in database but not in XML continue; } $databaseDiff = DatabaseComparator::computeDiff($database, $appDataFromXml->getDatabase($name)); if (!$databaseDiff) { if ($input->getOption('verbose')) { $output->writeln(sprintf('Same XML and database structures for datasource "%s" - no diff to generate', $name)); } continue; } $output->writeln(sprintf('Structure of database was modified in datasource "%s": %s', $name, $databaseDiff->getDescription())); $platform = $generatorConfig->getConfiguredPlatform(null, $name); $migrationsUp[$name] = $platform->getModifyDatabaseDDL($databaseDiff); $migrationsDown[$name] = $platform->getModifyDatabaseDDL($databaseDiff->getReverseDiff()); } if (!$migrationsUp) { $output->writeln('Same XML and database structures for all datasource - no diff to generate'); return; } $timestamp = time(); $migrationFileName = $manager->getMigrationFileName($timestamp); $migrationClassBody = $manager->getMigrationClassBody($migrationsUp, $migrationsDown, $timestamp); $file = $input->getOption('output-dir') . DIRECTORY_SEPARATOR . $migrationFileName; file_put_contents($file, $migrationClassBody); $output->writeln(sprintf('"%s" file successfully created.', $file)); if (null !== ($editorCmd = $input->getOption('editor'))) { $output->writeln(sprintf('Using "%s" as text editor', $editorCmd)); shell_exec($editorCmd . ' ' . escapeshellarg($file)); } else { $output->writeln('Please review the generated SQL statements, and add data migration code if necessary.'); $output->writeln('Once the migration class is valid, call the "migrate" task to execute it.'); } }