/** * @param string $xml * * @return Database */ public function applyXml($xml) { $this->readDatabase(); $builder = new QuickBuilder(); $builder->setPlatform($this->database->getPlatform()); $builder->setSchema($xml); $database = $builder->getDatabase(); $database->setSchema('migration'); $database->setPlatform($this->database->getPlatform()); $diff = DatabaseComparator::computeDiff($this->database, $database); if (false === $diff) { return null; } $sql = $this->database->getPlatform()->getModifyDatabaseDDL($diff); $this->con->beginTransaction(); $statements = SqlParser::parseString($sql); foreach ($statements as $statement) { try { $stmt = $this->con->prepare($statement); $stmt->execute(); } catch (\Exception $e) { $this->con->rollBack(); throw new BuildException(sprintf("Can not execute SQL: \n%s\nFrom database: \n%s\n\nTo database: \n%s\n", $statement, $this->database, $database), null, $e); } } $this->con->commit(); return $database; }
/** * @param string $xml * * @return Database|boolean */ public function applyXml($xml, $changeRequired = false) { $this->readDatabase(); $builder = new QuickBuilder(); $builder->setIdentifierQuoting(true); $builder->setPlatform($this->database->getPlatform()); $builder->setSchema($xml); $database = $builder->getDatabase(); $database->setSchema('migration'); $database->setPlatform($this->database->getPlatform()); $diff = DatabaseComparator::computeDiff($this->database, $database); if (false === $diff) { if ($changeRequired) { throw new BuildException(sprintf("No changes in schema to current database: \nSchema database:\n%s\n\nCurrent Database:\n%s", $database, $this->database)); } return false; } $sql = $this->database->getPlatform()->getModifyDatabaseDDL($diff); $this->con->beginTransaction(); if (!$sql) { throw new BuildException(sprintf('Ooops. There is a diff between current database and schema xml but no SQL has been generated. Change: %s', $diff)); } $statements = SqlParser::parseString($sql); foreach ($statements as $statement) { try { $stmt = $this->con->prepare($statement); $stmt->execute(); } catch (\Exception $e) { throw new BuildException(sprintf("Can not execute SQL: \n%s\nFrom database: \n%s\n\nTo database: \n%s\n", $statement, $this->database, $database), null, $e); } } $this->con->commit(); return $database; }
/** * Main method builds all the targets for a typical propel project. */ public function main() { $manager = new MigrationManager(); $manager->setConnections($this->getGeneratorConfig()->getBuildConnections()); $manager->setMigrationTable($this->getMigrationTable()); $manager->setMigrationDir($this->getOutputDirectory()); $previousTimestamps = $manager->getAlreadyExecutedMigrationTimestamps(); if (!($nextMigrationTimestamp = array_pop($previousTimestamps))) { $this->log('No migration were ever executed on this database - nothing to reverse.'); return false; } $this->log(sprintf('Executing migration %s down', $manager->getMigrationClassName($nextMigrationTimestamp))); if ($nbPreviousTimestamps = count($previousTimestamps)) { $previousTimestamp = array_pop($previousTimestamps); } else { $previousTimestamp = 0; } $migration = $manager->getMigrationObject($nextMigrationTimestamp); if (false === $migration->preDown($manager)) { $this->log('preDown() returned false. Aborting migration.', Project::MSG_ERR); return false; } foreach ($migration->getDownSQL() as $datasource => $sql) { $connection = $manager->getConnection($datasource); $this->log(sprintf('Connecting to database "%s" using DSN "%s"', $datasource, $connection['dsn']), Project::MSG_VERBOSE); $pdo = $manager->getPdoConnection($datasource); $res = 0; $statements = SqlParser::parseString($sql); foreach ($statements as $statement) { try { $this->log(sprintf('Executing statement "%s"', $statement), Project::MSG_VERBOSE); $stmt = $pdo->prepare($statement); $stmt->execute(); $res++; } catch (PDOException $e) { $this->log(sprintf('Failed to execute SQL "%s"', $statement), Project::MSG_ERR); // continue } } if (!$res) { $this->log('No statement was executed. The version was not updated.'); $this->log(sprintf('Please review the code in "%s"', $manager->getMigrationDir() . DIRECTORY_SEPARATOR . $manager->getMigrationClassName($nextMigrationTimestamp))); $this->log('Migration aborted', Project::MSG_ERR); return false; } $this->log(sprintf('%d of %d SQL statements executed successfully on datasource "%s"', $res, count($statements), $datasource)); $manager->updateLatestMigrationTimestamp($datasource, $previousTimestamp); $this->log(sprintf('Downgraded migration date to %d for datasource "%s"', $previousTimestamp, $datasource), Project::MSG_VERBOSE); } $migration->postDown($manager); if ($nbPreviousTimestamps) { $this->log(sprintf('Reverse migration complete. %d more migrations available for reverse.', $nbPreviousTimestamps)); } else { $this->log('Reverse migration complete. No more migration available for reverse'); } }
/** * 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(); } }
/** * @dataProvider explodeIntoStatementsDataProvider */ public function testExplodeIntoStatements($input, $output) { $parser = new SqlParser(); $parser->setSQL($input); $this->assertEquals($output, $parser->explodeIntoStatements()); }
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; }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $configOptions = array(); if ($this->hasInputOption('output-dir', $input)) { $configOptions['propel']['paths']['migrationDir'] = $input->getOption('output-dir'); } if ($this->hasInputOption('migration-table', $input)) { $configOptions['propel']['migrations']['tableName'] = $input->getOption('migration-table'); } $generatorConfig = $this->getGeneratorConfig($configOptions, $input); $this->createDirectory($generatorConfig->getSection('paths')['migrationDir']); $manager = new MigrationManager(); $manager->setGeneratorConfig($generatorConfig); $connections = array(); $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(array('dsn' => $dsn), $infos); } } $manager->setConnections($connections); $manager->setMigrationTable($generatorConfig->getSection('migrations')['tableName']); $manager->setWorkingDirectory($generatorConfig->getSection('paths')['migrationDir']); if (!($nextMigrationTimestamp = $manager->getFirstUpMigrationTimestamp())) { $output->writeln('All migrations were already executed - nothing to migrate.'); return false; } if ($input->getOption('fake')) { $output->writeln(sprintf('Faking migration %s up', $manager->getMigrationClassName($nextMigrationTimestamp))); } else { $output->writeln(sprintf('Executing migration %s up', $manager->getMigrationClassName($nextMigrationTimestamp))); } $migration = $manager->getMigrationObject($nextMigrationTimestamp); if (!$input->getOption('fake')) { if (false === $migration->preUp($manager)) { if ($input->getOption('force')) { $output->writeln('<error>preUp() returned false. Continue migration.</error>'); } else { $output->writeln('<error>preUp() returned false. Aborting migration.</error>'); return false; } } } foreach ($migration->getUpSQL() as $datasource => $sql) { $connection = $manager->getConnection($datasource); if ($input->getOption('verbose')) { $output->writeln(sprintf('Connecting to database "%s" using DSN "%s"', $datasource, $connection['dsn'])); } $conn = $manager->getAdapterConnection($datasource); $res = 0; $statements = SqlParser::parseString($sql); if (!$input->getOption('fake')) { foreach ($statements as $statement) { try { if ($input->getOption('verbose')) { $output->writeln(sprintf('Executing statement "%s"', $statement)); } $conn->exec($statement); $res++; } catch (\Exception $e) { if ($input->getOption('force')) { //continue, but print error message $output->writeln(sprintf('<error>Failed to execute SQL "%s". Continue migration.</error>', $statement)); } else { throw new RuntimeException(sprintf('<error>Failed to execute SQL "%s". Aborting migration.</error>', $statement), 0, $e); } } } //make sure foreign_keys are activated again in mysql $output->writeln(sprintf('%d of %d SQL statements executed successfully on datasource "%s"', $res, count($statements), $datasource)); } $manager->updateLatestMigrationTimestamp($datasource, $nextMigrationTimestamp); if ($input->getOption('verbose')) { $output->writeln(sprintf('Updated latest migration date to %d for datasource "%s"', $nextMigrationTimestamp, $datasource)); } } if (!$input->getOption('fake')) { $migration->postUp($manager); } if ($timestamps = $manager->getValidMigrationTimestamps()) { $output->writeln(sprintf('Migration complete. %d migrations left to execute.', count($timestamps))); } else { $output->writeln('Migration complete. No further migration to execute.'); } }
public function buildSQL(ConnectionInterface $con) { $statements = SqlParser::parseString($this->getSQL()); foreach ($statements as $statement) { if (strpos($statement, 'DROP') === 0) { // drop statements cause errors since the table doesn't exist continue; } $stmt = $con->prepare($statement); if ($stmt instanceof StatementInterface) { // only execute if has no error $stmt->execute(); } } return count($statements); }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $configOptions = array(); if ($this->hasInputOption('output-dir', $input)) { $configOptions['propel']['paths']['migrationDir'] = $input->getOption('output-dir'); } if ($this->hasInputOption('migration-table', $input)) { $configOptions['propel']['migrations']['tableName'] = $input->getOption('migration-table'); } $generatorConfig = $this->getGeneratorConfig($configOptions, $input); $this->createDirectory($generatorConfig->getSection('paths')['migrationDir']); $manager = new MigrationManager(); $manager->setGeneratorConfig($generatorConfig); $connections = array(); $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(array('dsn' => $dsn), $infos); } } $manager->setConnections($connections); $manager->setMigrationTable($generatorConfig->getSection('migrations')['tableName']); $manager->setWorkingDirectory($generatorConfig->getSection('paths')['migrationDir']); $previousTimestamps = $manager->getAlreadyExecutedMigrationTimestamps(); if (!($nextMigrationTimestamp = array_pop($previousTimestamps))) { $output->writeln('No migration were ever executed on this database - nothing to reverse.'); return false; } $output->writeln(sprintf('Executing migration %s down', $manager->getMigrationClassName($nextMigrationTimestamp))); if ($nbPreviousTimestamps = count($previousTimestamps)) { $previousTimestamp = array_pop($previousTimestamps); } else { $previousTimestamp = 0; } $migration = $manager->getMigrationObject($nextMigrationTimestamp); if (!$input->getOption('fake')) { if (false === $migration->preDown($manager)) { if ($input->getOption('force')) { $output->writeln('<error>preDown() returned false. Continue migration.</error>'); } else { $output->writeln('<error>preDown() returned false. Aborting migration.</error>'); return false; } } } foreach ($migration->getDownSQL() as $datasource => $sql) { $connection = $manager->getConnection($datasource); if ($input->getOption('verbose')) { $output->writeln(sprintf('Connecting to database "%s" using DSN "%s"', $datasource, $connection['dsn'])); } $conn = $manager->getAdapterConnection($datasource); $res = 0; $statements = SqlParser::parseString($sql); if (!$input->getOption('fake')) { foreach ($statements as $statement) { try { if ($input->getOption('verbose')) { $output->writeln(sprintf('Executing statement "%s"', $statement)); } $conn->exec($statement); $res++; } catch (\Exception $e) { if ($input->getOption('force')) { //continue, but print error message $output->writeln(sprintf('<error>Failed to execute SQL "%s". Continue migration.</error>', $statement)); } else { throw new RuntimeException(sprintf('<error>Failed to execute SQL "%s". Aborting migration.</error>', $statement), 0, $e); } } } $output->writeln(sprintf('%d of %d SQL statements executed successfully on datasource "%s"', $res, count($statements), $datasource)); } $manager->removeMigrationTimestamp($datasource, $nextMigrationTimestamp); if ($input->getOption('verbose')) { $output->writeln(sprintf('Downgraded migration date to %d for datasource "%s"', $previousTimestamp, $datasource)); } } if (!$input->getOption('fake')) { $migration->postDown($manager); } if ($nbPreviousTimestamps) { $output->writeln(sprintf('Reverse migration complete. %d more migrations available for reverse.', $nbPreviousTimestamps)); } else { $output->writeln('Reverse migration complete. No more migration available for reverse'); } }
public function createMigrationTable($datasource) { $platform = $this->getPlatform($datasource); // modelize the table $database = new Database($datasource); $database->setPlatform($platform); $table = new Table($this->getMigrationTable()); $database->addTable($table); $column = new Column('version'); $column->getDomain()->copy($platform->getDomainForType('INTEGER')); $column->setDefaultValue(0); $table->addColumn($column); // insert the table into the database $statements = $platform->getAddTableDDL($table); $conn = $this->getAdapterConnection($datasource); $res = SqlParser::executeString($statements, $conn); if (!$res) { throw new \Exception(sprintf('Unable to create migration table in datasource "%s"', $datasource)); } }
/** * @param string $datasource A datasource name. */ public function insertSql($datasource = null) { $statementsToInsert = array(); foreach ($this->getProperties($this->getSqlDbMapFilename()) as $sqlFile => $database) { if (null !== $datasource && $database !== $datasource) { // skip $this->log(sprintf('Skipping %s.', $sqlFile)); break; } if (!isset($statementsToInsert[$database])) { $statementsToInsert[$database] = array(); } if (null === $datasource || null !== $database && $database === $datasource) { $filename = $this->getWorkingDirectory() . DIRECTORY_SEPARATOR . $sqlFile; if (file_exists($filename)) { foreach (SqlParser::parseFile($filename) as $sql) { $statementsToInsert[$database][] = $sql; } } else { $this->log(sprintf("File %s doesn't exist", $filename)); } } } foreach ($statementsToInsert as $database => $sqls) { if (!$this->hasConnection($database)) { $this->log(sprintf("No connection available for %s database", $database)); continue; } $con = $this->getConnectionInstance($database); $con->transaction(function () use($con, $sqls) { foreach ($sqls as $sql) { try { $stmt = $con->prepare($sql); $stmt->execute(); } catch (\Exception $e) { $message = sprintf('SQL insert failed: %s', $sql); throw new \Exception($message, 0, $e); } } }); $this->log(sprintf('%d queries executed for %s database.', count($sqls), $database)); } return true; }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $generatorConfig = $this->getGeneratorConfig(array('propel.platform.class' => $input->getOption('platform')), $input); $this->createDirectory($input->getOption('output-dir')); $manager = new MigrationManager(); $manager->setGeneratorConfig($generatorConfig); $connections = array(); foreach ($input->getOption('connection') 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')); $previousTimestamps = $manager->getAlreadyExecutedMigrationTimestamps(); if (!($nextMigrationTimestamp = array_pop($previousTimestamps))) { $output->writeln('No migration were ever executed on this database - nothing to reverse.'); return false; } $output->writeln(sprintf('Executing migration %s down', $manager->getMigrationClassName($nextMigrationTimestamp))); if ($nbPreviousTimestamps = count($previousTimestamps)) { $previousTimestamp = array_pop($previousTimestamps); } else { $previousTimestamp = 0; } $migration = $manager->getMigrationObject($nextMigrationTimestamp); if (false === $migration->preDown($manager)) { $output->writeln('<error>preDown() returned false. Aborting migration.</error>'); return false; } foreach ($migration->getDownSQL() as $datasource => $sql) { $connection = $manager->getConnection($datasource); if ($input->getOption('verbose')) { $output->writeln(sprintf('Connecting to database "%s" using DSN "%s"', $datasource, $connection['dsn'])); } $conn = $manager->getAdapterConnection($datasource); $res = 0; $statements = SqlParser::parseString($sql); foreach ($statements as $statement) { try { if ($input->getOption('verbose')) { $output->writeln(sprintf('Executing statement "%s"', $statement)); } $stmt = $conn->prepare($statement); $stmt->execute(); $res++; } catch (PDOException $e) { $output->writeln(sprintf('<error>Failed to execute SQL "%s"</error>', $statement)); } } if (!$res) { $output->writeln('No statement was executed. The version was not updated.'); $output->writeln(sprintf('Please review the code in "%s"', $manager->getMigrationDir() . DIRECTORY_SEPARATOR . $manager->getMigrationClassName($nextMigrationTimestamp))); $output->writeln('<error>Migration aborted</error>'); return false; } $output->writeln(sprintf('%d of %d SQL statements executed successfully on datasource "%s"', $res, count($statements), $datasource)); $manager->updateLatestMigrationTimestamp($datasource, $previousTimestamp); if ($input->getOption('verbose')) { $output->writeln(sprintf('Downgraded migration date to %d for datasource "%s"', $previousTimestamp, $datasource)); } } $migration->postDown($manager); if ($nbPreviousTimestamps) { $output->writeln(sprintf('Reverse migration complete. %d more migrations available for reverse.', $nbPreviousTimestamps)); } else { $output->writeln('Reverse migration complete. No more migration available for reverse'); } }
/** * Load the sql file and then execute it * * @throws BuildException */ public function main() { $conf = new GeneratorConfig(); $conf->setBuildProperties($this->getProject()->getProperties()); $this->setBuildConnections($conf->getBuildConnections()); if ($this->sqldbmap === null || $this->getSqlDbMap()->exists() === false) { throw new BuildException("You haven't provided an sqldbmap, or " . "the one you specified doesn't exist: " . $this->sqldbmap->getPath()); } if ($this->url === null) { throw new BuildException("DSN url attribute must be set!"); } // get an ordered list of SQL files to execute $databases = $this->getFilesToExecute(); $this->log(sprintf('Reading SQL files...')); foreach ($databases as $database => $files) { $statements[$database] = array(); foreach ($files as $fileName) { $fullFileName = $this->srcDir ? $this->srcDir . DIRECTORY_SEPARATOR . $fileName : $fileName; if (file_exists($fullFileName)) { $this->log(sprintf(' Loading statements from "%s"', $fullFileName)); $fileStatements = SqlParser::parseFile($fullFileName); $this->log(sprintf(' %d statements to execute', count($fileStatements)), Project::MSG_VERBOSE); $statements[$database] = array_merge($statements[$database], $fileStatements); } else { $this->log(sprintf('File "%s" in sqldbmap does not exist, skipping it.', $fullFileName)); } } } $successfullStatements = 0; $this->log(sprintf('Executing SQL statements...')); foreach ($statements as $database => $statementList) { $successfullStatements += $this->insertDatabaseSqlFiles($database, $statementList); } $this->log(sprintf('SQL execution complete. %d statements successfully executed.', $successfullStatements)); }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $configOptions = array(); 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); $connections = array(); $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(array('dsn' => $dsn), $infos); } } $manager->setConnections($connections); $manager->setMigrationTable($input->getOption('migration-table')); $manager->setWorkingDirectory($generatorConfig->getSection('paths')['migrationDir']); if (!$manager->getFirstUpMigrationTimestamp()) { $output->writeln('All migrations were already executed - nothing to migrate.'); return false; } $timestamps = $manager->getValidMigrationTimestamps(); if (count($timestamps) > 1) { $output->writeln(sprintf('%d migrations to execute', count($timestamps))); } foreach ($timestamps as $timestamp) { $output->writeln(sprintf('Executing migration %s up', $manager->getMigrationClassName($timestamp))); $migration = $manager->getMigrationObject($timestamp); if (property_exists($migration, 'comment') && $migration->comment) { $output->writeln(sprintf('<info>%s</info>', $migration->comment)); } if (false === $migration->preUp($manager)) { $output->writeln('<error>preUp() returned false. Aborting migration.</error>'); return false; } foreach ($migration->getUpSQL() as $datasource => $sql) { $connection = $manager->getConnection($datasource); if ($input->getOption('verbose')) { $output->writeln(sprintf('Connecting to database "%s" using DSN "%s"', $datasource, $connection['dsn'])); } $conn = $manager->getAdapterConnection($datasource); $res = 0; $statements = SqlParser::parseString($sql); foreach ($statements as $statement) { try { if ($input->getOption('verbose')) { $output->writeln(sprintf('Executing statement "%s"', $statement)); } $stmt = $conn->prepare($statement); $stmt->execute(); $res++; } catch (\PDOException $e) { $output->writeln(sprintf('<error>Failed to execute SQL "%s": %s</error>', $statement, $e->getMessage())); // continue } } if (!$res) { $output->writeln('No statement was executed. The version was not updated.'); $output->writeln(sprintf('Please review the code in "%s"', $manager->getWorkingDirectory() . DIRECTORY_SEPARATOR . $manager->getMigrationClassName($timestamp))); $output->writeln('<error>Migration aborted</error>'); return false; } $output->writeln(sprintf('%d of %d SQL statements executed successfully on datasource "%s"', $res, count($statements), $datasource)); } // migrations for datasources have passed - update the timestamp // for all datasources foreach ($manager->getConnections() as $datasource => $connection) { $manager->updateLatestMigrationTimestamp($datasource, $timestamp); if ($input->getOption('verbose')) { $output->writeln(sprintf('Updated latest migration date to %d for datasource "%s"', $timestamp, $datasource)); } } $migration->postUp($manager); } $output->writeln('Migration complete. No further migration to execute.'); }
/** * @param string $datasource A datasource name. */ public function insertSql($datasource = null) { $statementsToInsert = array(); foreach ($this->getProperties($this->getSqlDbMapFilename()) as $sqlFile => $database) { if (null !== $datasource && $database !== $datasource) { // skip break; } if (!isset($statementsToInsert[$database])) { $statementsToInsert[$database] = array(); } if (null === $database || null !== $database && $database === $datasource) { $filename = $this->getWorkingDirectory() . DIRECTORY_SEPARATOR . $sqlFile; if (file_exists($filename)) { foreach (SqlParser::parseFile($filename) as $sql) { $statementsToInsert[$database][] = $sql; } } } } foreach ($statementsToInsert as $database => $sqls) { if (!$this->hasConnection($database)) { continue; } $pdo = $this->getPdoConnection($database); $pdo->beginTransaction(); try { foreach ($sqls as $sql) { $stmt = $pdo->prepare($sql); $stmt->execute(); } $pdo->commit(); } catch (PDOException $e) { $pdo->rollback(); throw $e; } } return true; }