From bdf49341f9cea7a6da703e930c4e136176db7a6c Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Thu, 11 May 2017 19:12:34 -0300 Subject: [PATCH 01/12] Add the version status to indicate if the migrate is complete or fail! --- scripts/migrate | 1 + src/Commands/AbstractCommand.php | 39 ++++++++++++--- src/Commands/CommandInterface.php | 4 +- src/Commands/DblibCommand.php | 2 +- src/Commands/MysqlCommand.php | 2 +- src/Commands/PgsqlCommand.php | 2 +- src/Commands/SqliteCommand.php | 2 +- src/Console/DatabaseVersionCommand.php | 4 +- src/Console/InstallCommand.php | 47 +++++++++++++++++++ .../DatabaseNotVersionedException.php | 14 ++++++ src/Exception/OldVersionSchemaException.php | 14 ++++++ src/Migration.php | 23 +++++++-- tests/BaseCommand.php | 6 +++ 13 files changed, 142 insertions(+), 18 deletions(-) create mode 100644 src/Console/InstallCommand.php create mode 100644 src/Exception/DatabaseNotVersionedException.php create mode 100644 src/Exception/OldVersionSchemaException.php diff --git a/scripts/migrate b/scripts/migrate index 371836e..98519c6 100755 --- a/scripts/migrate +++ b/scripts/migrate @@ -19,4 +19,5 @@ $application->add(new \ByJG\DbMigration\Console\UpCommand()); $application->add(new \ByJG\DbMigration\Console\DownCommand()); $application->add(new \ByJG\DbMigration\Console\CreateCommand()); $application->add(new \ByJG\DbMigration\Console\DatabaseVersionCommand()); +$application->add(new \ByJG\DbMigration\Console\InstallCommand()); $application->run(); diff --git a/src/Commands/AbstractCommand.php b/src/Commands/AbstractCommand.php index 1a02759..2ad5896 100644 --- a/src/Commands/AbstractCommand.php +++ b/src/Commands/AbstractCommand.php @@ -3,6 +3,8 @@ namespace ByJG\DbMigration\Commands; use ByJG\AnyDataset\DbDriverInterface; +use ByJG\DbMigration\Exception\DatabaseNotVersionedException; +use ByJG\DbMigration\Exception\OldVersionSchemaException; abstract class AbstractCommand implements CommandInterface { @@ -31,24 +33,47 @@ public function getDbDriver() public function getVersion() { + $result = []; try { - return $this->getDbDriver()->getScalar('SELECT version FROM migration_version'); + $result['version'] = $this->getDbDriver()->getScalar('SELECT version FROM migration_version'); } catch (\Exception $ex) { - throw new \Exception('This database does not have a migration version. Please use "migrate reset" to create one.'); + throw new DatabaseNotVersionedException('This database does not have a migration version. Please use "migrate reset" or "migrate install" to create one.'); } + + try { + $result['status'] = $this->getDbDriver()->getScalar('SELECT status FROM migration_version'); + } catch (\Exception $ex) { + throw new OldVersionSchemaException('This database does not have a migration version. Please use "migrate install" for update it.'); + } + + return $result; } - public function setVersion($version) + public function setVersion($version, $status) { - $this->getDbDriver()->execute('UPDATE migration_version SET version = :version', ['version' => $version]); + $this->getDbDriver()->execute( + 'UPDATE migration_version SET version = :version, status = :status', + [ + 'version' => $version, + 'status' => $status + ] + ); } protected function checkExistsVersion() { // Get the version to check if exists - $version = $this->getVersion(); - if (empty($version)) { - $this->getDbDriver()->execute('insert into migration_version values(0)'); + $versionInfo = $this->getVersion(); + if (empty($versionInfo['version'])) { + $this->getDbDriver()->execute("insert into migration_version values(0, 'unknow')"); } } + + public function updateVersionTable() + { + $currentVersion = $this->getDbDriver()->getScalar('select version from migration_version'); + $this->getDbDriver()->execute('drop table migration_version'); + $this->createVersion(); + $this->setVersion($currentVersion, 'unknow'); + } } diff --git a/src/Commands/CommandInterface.php b/src/Commands/CommandInterface.php index 40c3771..8b080f6 100644 --- a/src/Commands/CommandInterface.php +++ b/src/Commands/CommandInterface.php @@ -13,10 +13,12 @@ public function createDatabase(); public function dropDatabase(); public function getVersion(); + + public function updateVersionTable(); public function executeSql($sql); - public function setVersion($version); + public function setVersion($version, $status); public function createVersion(); } diff --git a/src/Commands/DblibCommand.php b/src/Commands/DblibCommand.php index a4260f9..ecca522 100644 --- a/src/Commands/DblibCommand.php +++ b/src/Commands/DblibCommand.php @@ -53,7 +53,7 @@ public function createVersion() { $database = preg_replace('~^/~', '', $this->getDbDriver()->getUri()->getPath()); $table = 'migration_version'; - $createTable = 'CREATE TABLE migration_version (version int)'; + $createTable = 'CREATE TABLE migration_version (version int, status varchar(20))'; $this->createTableIfNotExists($database, $table, $createTable); $this->checkExistsVersion(); } diff --git a/src/Commands/MysqlCommand.php b/src/Commands/MysqlCommand.php index 5ed9e27..83824b9 100644 --- a/src/Commands/MysqlCommand.php +++ b/src/Commands/MysqlCommand.php @@ -35,7 +35,7 @@ public function dropDatabase() public function createVersion() { - $this->getDbDriver()->execute('CREATE TABLE IF NOT EXISTS migration_version (version int)'); + $this->getDbDriver()->execute('CREATE TABLE IF NOT EXISTS migration_version (version int, status varchar(20))'); $this->checkExistsVersion(); } diff --git a/src/Commands/PgsqlCommand.php b/src/Commands/PgsqlCommand.php index 460f76c..c743b8a 100644 --- a/src/Commands/PgsqlCommand.php +++ b/src/Commands/PgsqlCommand.php @@ -53,7 +53,7 @@ public function dropDatabase() public function createVersion() { - $this->getDbDriver()->execute('CREATE TABLE IF NOT EXISTS migration_version (version int)'); + $this->getDbDriver()->execute('CREATE TABLE IF NOT EXISTS migration_version (version int, status varchar(20))'); $this->checkExistsVersion(); } diff --git a/src/Commands/SqliteCommand.php b/src/Commands/SqliteCommand.php index f05ac7b..dc361d2 100644 --- a/src/Commands/SqliteCommand.php +++ b/src/Commands/SqliteCommand.php @@ -32,7 +32,7 @@ public function dropDatabase() public function createVersion() { - $this->getDbDriver()->execute('CREATE TABLE IF NOT EXISTS migration_version (version int)'); + $this->getDbDriver()->execute('CREATE TABLE IF NOT EXISTS migration_version (version int, status varchar(20))'); $this->checkExistsVersion(); } diff --git a/src/Console/DatabaseVersionCommand.php b/src/Console/DatabaseVersionCommand.php index a1d8830..9375c2b 100644 --- a/src/Console/DatabaseVersionCommand.php +++ b/src/Console/DatabaseVersionCommand.php @@ -25,6 +25,8 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { parent::execute($input, $output); - $output->writeln('version: ' . $this->migration->getCurrentVersion()); + $versionInfo = $this->migration->getCurrentVersion(); + $output->writeln('version: ' . $versionInfo['version']); + $output->writeln('status.: ' . $versionInfo['status']); } } diff --git a/src/Console/InstallCommand.php b/src/Console/InstallCommand.php new file mode 100644 index 0000000..58aa3c4 --- /dev/null +++ b/src/Console/InstallCommand.php @@ -0,0 +1,47 @@ +setName('install') + ->setDescription('Install or upgrade the migrate version in a existing database'); + + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + parent::execute($input, $output); + + $action = 'Database is already versioned. '; + try { + $this->migration->getCurrentVersion(); + } catch (DatabaseNotVersionedException $ex) { + $action = 'Created the version table'; + $this->migration->createVersion(); + } catch (OldVersionSchemaException $ex) { + $action = 'Updated the version table'; + $this->migration->updateTableVersion(); + } + + $version = $this->migration->getCurrentVersion(); + $output->writeln($action); + $output->writeln('current version: ' . $version['version']); + $output->writeln('current status.: ' . $version['status']); + } +} diff --git a/src/Exception/DatabaseNotVersionedException.php b/src/Exception/DatabaseNotVersionedException.php new file mode 100644 index 0000000..5b3a29d --- /dev/null +++ b/src/Exception/DatabaseNotVersionedException.php @@ -0,0 +1,14 @@ +getDbCommand()->dropDatabase(); $this->getDbCommand()->createDatabase(); - $this->getDbCommand()->executeSql(file_get_contents($this->getBaseSql())); $this->getDbCommand()->createVersion(); + $this->getDbCommand()->executeSql(file_get_contents($this->getBaseSql())); + $this->getDbCommand()->setVersion(0, 'complete'); $this->up($upVersion); } + public function createVersion() + { + $this->getDbCommand()->createVersion(); + } + + public function updateTableVersion() + { + $this->getDbCommand()->updateVersionTable(); + } + /** * Get the current database version * @@ -145,7 +156,7 @@ public function reset($upVersion = null) */ public function getCurrentVersion() { - return intval($this->getDbCommand()->getVersion()); + return $this->getDbCommand()->getVersion();; } /** @@ -173,8 +184,9 @@ protected function canContinue($currentVersion, $upVersion, $increment) */ protected function migrate($upVersion, $increment) { - $currentVersion = $this->getCurrentVersion() + $increment; - + $versionInfo = $this->getCurrentVersion(); + $currentVersion = intval($versionInfo['version']) + $increment; + while ($this->canContinue($currentVersion, $upVersion, $increment) && file_exists($file = $this->getMigrationSql($currentVersion, $increment)) ) { @@ -182,8 +194,9 @@ protected function migrate($upVersion, $increment) call_user_func_array($this->_callableProgress, ['migrate', $currentVersion]); } + $this->getDbCommand()->setVersion($currentVersion, 'partial ' . ($increment>0 ? 'up' : 'down')); $this->getDbCommand()->executeSql(file_get_contents($file)); - $this->getDbCommand()->setVersion($currentVersion); + $this->getDbCommand()->setVersion($currentVersion, 'complete'); $currentVersion = $currentVersion + $increment; } } diff --git a/tests/BaseCommand.php b/tests/BaseCommand.php index 434b96a..31158cc 100644 --- a/tests/BaseCommand.php +++ b/tests/BaseCommand.php @@ -84,6 +84,8 @@ protected function assertVersion0() { $version = $this->migrate->getDbDriver()->getScalar('select version from migration_version'); $this->assertEquals(0, $version); + $status = $this->migrate->getDbDriver()->getScalar('select status from migration_version'); + $this->assertEquals('complete', $status); $iterator = $this->migrate->getDbDriver()->getIterator('select * from users'); @@ -114,6 +116,8 @@ protected function assertVersion1() { $version = $this->migrate->getDbDriver()->getScalar('select version from migration_version'); $this->assertEquals(1, $version); + $status = $this->migrate->getDbDriver()->getScalar('select status from migration_version'); + $this->assertEquals('complete', $status); $iterator = $this->migrate->getDbDriver()->getIterator('select * from users'); @@ -144,6 +148,8 @@ protected function assertVersion2() { $version = $this->migrate->getDbDriver()->getScalar('select version from migration_version'); $this->assertEquals(2, $version); + $status = $this->migrate->getDbDriver()->getScalar('select status from migration_version'); + $this->assertEquals('complete', $status); $iterator = $this->migrate->getDbDriver()->getIterator('select * from users'); From 98ccd970f8d673a55f4d0f92e60a15a0ad94f31e Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Thu, 11 May 2017 19:29:06 -0300 Subject: [PATCH 02/12] Better management of the current database status; --- src/Console/DownCommand.php | 18 +++++++++++++++++- src/Console/UpCommand.php | 18 +++++++++++++++++- .../DatabaseIsIncompleteException.php | 14 ++++++++++++++ src/Migration.php | 19 ++++++++++++++----- 4 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 src/Exception/DatabaseIsIncompleteException.php diff --git a/src/Console/DownCommand.php b/src/Console/DownCommand.php index 1f924b0..00bee71 100644 --- a/src/Console/DownCommand.php +++ b/src/Console/DownCommand.php @@ -10,6 +10,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ConfirmationQuestion; class DownCommand extends ConsoleCommand { @@ -24,8 +25,23 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { + $versionInfo = $this->migration->getCurrentVersion(); + if (strpos($versionInfo['status'], 'partial') !== false) { + $helper = $this->getHelper('question'); + $question = new ConfirmationQuestion( + 'The database was not fully updated and maybe be unstable. Did you really want migrate the version? (y/N)', + false + ); + + if (!$helper->ask($input, $output, $question)) { + $output->writeln('Aborted.'); + + return; + } + } + parent::execute($input, $output); - $this->migration->down($this->upTo); + $this->migration->down($this->upTo, true); } } diff --git a/src/Console/UpCommand.php b/src/Console/UpCommand.php index 0dcad72..e1907e5 100644 --- a/src/Console/UpCommand.php +++ b/src/Console/UpCommand.php @@ -10,6 +10,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ConfirmationQuestion; class UpCommand extends ConsoleCommand { @@ -24,7 +25,22 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { + $versionInfo = $this->migration->getCurrentVersion(); + if (strpos($versionInfo['status'], 'partial') !== false) { + $helper = $this->getHelper('question'); + $question = new ConfirmationQuestion( + 'The database was not fully updated and maybe be unstable. Did you really want migrate the version? (y/N) ', + false + ); + + if (!$helper->ask($input, $output, $question)) { + $output->writeln('Aborted.'); + + return; + } + } + parent::execute($input, $output); - $this->migration->up($this->upTo); + $this->migration->up($this->upTo, true); } } diff --git a/src/Exception/DatabaseIsIncompleteException.php b/src/Exception/DatabaseIsIncompleteException.php new file mode 100644 index 0000000..fbe6df1 --- /dev/null +++ b/src/Exception/DatabaseIsIncompleteException.php @@ -0,0 +1,14 @@ +getCurrentVersion(); $currentVersion = intval($versionInfo['version']) + $increment; + if (strpos($versionInfo['status'], 'partial') !== false && !$force) { + throw new DatabaseIsIncompleteException('Database was not fully updated. Use --force for migrate.'); + } + while ($this->canContinue($currentVersion, $upVersion, $increment) && file_exists($file = $this->getMigrationSql($currentVersion, $increment)) ) { @@ -205,20 +212,22 @@ protected function migrate($upVersion, $increment) * Run all scripts to up the database version from current up to latest version or the specified version. * * @param int $upVersion + * @param bool $force */ - public function up($upVersion = null) + public function up($upVersion = null, $force = false) { - $this->migrate($upVersion, 1); + $this->migrate($upVersion, 1, $force); } /** * Run all scripts to down the database version from current version up to the specified version. * * @param int $upVersion + * @param bool $force */ - public function down($upVersion) + public function down($upVersion, $force = false) { - $this->migrate($upVersion, -1); + $this->migrate($upVersion, -1, $force); } public function addCallbackProgress(Callable $callable) From 50cc7fec0b8a0581f5100978fd218fd76ed6215a Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Thu, 11 May 2017 19:29:47 -0300 Subject: [PATCH 03/12] Update the version number script; --- scripts/migrate | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/migrate b/scripts/migrate index 98519c6..d63ba43 100755 --- a/scripts/migrate +++ b/scripts/migrate @@ -13,7 +13,7 @@ require_once($autoload); use Symfony\Component\Console\Application; -$application = new Application('Migrate Script by JG', '1.1.1'); +$application = new Application('Migrate Script by JG', '2.0.0'); $application->add(new \ByJG\DbMigration\Console\ResetCommand()); $application->add(new \ByJG\DbMigration\Console\UpCommand()); $application->add(new \ByJG\DbMigration\Console\DownCommand()); From b5215b5f44f163642fb9d609a4ab5bbec4c808b6 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Mon, 15 May 2017 16:42:39 -0300 Subject: [PATCH 04/12] Added the DEV sql --- README.md | 26 ++++++++++++++++++++++++-- src/Migration.php | 12 +++++++++--- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 746f1ca..5e448e6 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,25 @@ For example: 00002.sql is the script for move the database from version '1' to ' For example: 00001.sql is the script for move the database from version '2' to '1'. The "down" folder is optional. +### Multi Development environment + +If you work with multiple developers and multiple branches it is to difficult to determine what is the next number. + +In that case you have the suffix "-dev" after the version number. + +See the scenario: + +- Developer 1 create a branch and the most recent version in e.g. 42. +- Developer 2 create a branch at the same time and have the same database version number. + +In both case the developers will create a file called 43-dev.sql. Both developers will migrate UP and DOWN with +no problem and your local version will be 43. + +But developer 1 merged your changes and created a final version 43.sql (`git mv 43-dev.sql 43.sql`). If the developer 2 +update your local branch he will have a file 43.sql (from dev 1) and your file 43-dev.sql. +If he is try to migrate UP or DOWN +the migration script will down and alert him there a TWO versions 43. In that case, developer 2 will have to update your +file do 44-dev.sql and continue to work until merge your changes and generate a final version. ## Running in the command line @@ -76,9 +95,12 @@ Usage: command [options] [arguments] Available commands: + create Create the directory structure FROM a pre-existing database + install Install or upgrade the migrate version in a existing database down Migrate down the database version. reset Create a fresh new database up Migrate Up the database version + version Get the current database version Arguments: connection The connection string. Ex. mysql://root:password@server/database [default: false] @@ -91,7 +113,7 @@ Example: migrate down --up-to=3 --path=/somepath mysql://root:password@server/database ``` -## Suportted databases: +## Supported databases: * Sqlite * Mysql / MariaDB @@ -101,7 +123,7 @@ Example: ## Installing Globally ```bash -composer global require 'byjg/migration=1.1.*' +composer global require 'byjg/migration=2.0.*' sudo ln -s $HOME/.composer/vendor/bin/migrate /usr/local/bin ``` diff --git a/src/Migration.php b/src/Migration.php index 139ff55..5db29b4 100644 --- a/src/Migration.php +++ b/src/Migration.php @@ -102,9 +102,15 @@ public function getMigrationSql($version, $increment) $this->_folder . "/migrations" . "/" . ($increment < 0 ? "down" : "up") - . "/*$version.sql" + . "/*\{$version,{$version}-dev\}.sql", + GLOB_BRACE ); + // Valid values are 0 or 1 + if (count($result) > 1) { + throw new \InvalidArgumentException("You have two files with the same version $version number"); + } + foreach ($result as $file) { if (intval(basename($file)) == $version) { return $file; @@ -170,8 +176,8 @@ protected function canContinue($currentVersion, $upVersion, $increment) { $existsUpVersion = ($upVersion !== null); $compareVersion = strcmp( - str_pad($currentVersion, 5, '0', STR_PAD_LEFT), - str_pad($upVersion, 5, '0', STR_PAD_LEFT) + str_pad($currentVersion, 10, '0', STR_PAD_LEFT), + str_pad($upVersion, 10, '0', STR_PAD_LEFT) ) == $increment; return !($existsUpVersion && $compareVersion); From b5a678348f7a52488474d58bedc175132613aeee Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Mon, 15 May 2017 16:52:16 -0300 Subject: [PATCH 05/12] Fixed GLOB_BRACE in Alpine. --- src/Migration.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Migration.php b/src/Migration.php index 5db29b4..abd7f41 100644 --- a/src/Migration.php +++ b/src/Migration.php @@ -98,12 +98,17 @@ public function getBaseSql() */ public function getMigrationSql($version, $increment) { - $result = glob( - $this->_folder + // I could use the GLOB_BRACE but it is not supported on ALPINE distros. + // So, I have to call multiple times to simulate the braces. + + $filePattern = $this->_folder . "/migrations" . "/" . ($increment < 0 ? "down" : "up") - . "/*\{$version,{$version}-dev\}.sql", - GLOB_BRACE + . "/*%s.sql"; + + $result = array_merge( + glob(sprintf($filePattern, $version)), + glob(sprintf($filePattern, "$version-dev")) ); // Valid values are 0 or 1 From d06758e6e537a27aebcaf1a622a5dc676fc05ba5 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Mon, 15 May 2017 17:29:03 -0300 Subject: [PATCH 06/12] Try to fix travis. --- src/Migration.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Migration.php b/src/Migration.php index abd7f41..091a2dc 100644 --- a/src/Migration.php +++ b/src/Migration.php @@ -101,13 +101,18 @@ public function getMigrationSql($version, $increment) // I could use the GLOB_BRACE but it is not supported on ALPINE distros. // So, I have to call multiple times to simulate the braces. + if (intval($version) != $version) { + throw new \InvalidArgumentException("Version '$version' should be a integer number"); + } + $version = intval($version); + $filePattern = $this->_folder . "/migrations" . "/" . ($increment < 0 ? "down" : "up") . "/*%s.sql"; $result = array_merge( - glob(sprintf($filePattern, $version)), + glob(sprintf($filePattern, "$version")), glob(sprintf($filePattern, "$version-dev")) ); @@ -117,7 +122,7 @@ public function getMigrationSql($version, $increment) } foreach ($result as $file) { - if (intval(basename($file)) == $version) { + if (intval(basename($file)) === $version) { return $file; } } From 6296c71084615a71a3a98efe014b181bea3cb205 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Mon, 15 May 2017 18:28:00 -0300 Subject: [PATCH 07/12] Try to fix travis. --- src/Migration.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Migration.php b/src/Migration.php index 091a2dc..8e3d016 100644 --- a/src/Migration.php +++ b/src/Migration.php @@ -185,12 +185,16 @@ public function getCurrentVersion() protected function canContinue($currentVersion, $upVersion, $increment) { $existsUpVersion = ($upVersion !== null); - $compareVersion = strcmp( - str_pad($currentVersion, 10, '0', STR_PAD_LEFT), - str_pad($upVersion, 10, '0', STR_PAD_LEFT) - ) == $increment; + $compareVersion = + intval($currentVersion) < intval($upVersion) + ? -1 + : ( + intval($currentVersion) > intval($upVersion) + ? 1 + : 0 + ); - return !($existsUpVersion && $compareVersion); + return !($existsUpVersion && ($compareVersion === intval($increment))); } /** From 4370cd19f5efad91d04e1e07423ac96602ab6b6e Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Tue, 23 May 2017 23:54:20 -0300 Subject: [PATCH 08/12] Add the UPDATE (the system choose if UP or DOWN the database --- scripts/migrate | 1 + src/Console/UpdateCommand.php | 47 +++++++++++++++++++++++++++++++++++ src/Migration.php | 18 ++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 src/Console/UpdateCommand.php diff --git a/scripts/migrate b/scripts/migrate index d63ba43..a66ff19 100755 --- a/scripts/migrate +++ b/scripts/migrate @@ -20,4 +20,5 @@ $application->add(new \ByJG\DbMigration\Console\DownCommand()); $application->add(new \ByJG\DbMigration\Console\CreateCommand()); $application->add(new \ByJG\DbMigration\Console\DatabaseVersionCommand()); $application->add(new \ByJG\DbMigration\Console\InstallCommand()); +$application->add(new \ByJG\DbMigration\Console\UpdateCommand()); $application->run(); diff --git a/src/Console/UpdateCommand.php b/src/Console/UpdateCommand.php new file mode 100644 index 0000000..859c8fd --- /dev/null +++ b/src/Console/UpdateCommand.php @@ -0,0 +1,47 @@ +setName('update') + ->setDescription('Migrate Up or Down the database version based on the current database version and the ' . + 'migration scripts available' + ); + + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $versionInfo = $this->migration->getCurrentVersion(); + if (strpos($versionInfo['status'], 'partial') !== false) { + $helper = $this->getHelper('question'); + $question = new ConfirmationQuestion( + 'The database was not fully updated and maybe be unstable. Did you really want migrate the version? (y/N) ', + false + ); + + if (!$helper->ask($input, $output, $question)) { + $output->writeln('Aborted.'); + return; + } + } + + parent::execute($input, $output); + $this->migration->update($this->upTo, true); + } +} diff --git a/src/Migration.php b/src/Migration.php index 8e3d016..e6fe093 100644 --- a/src/Migration.php +++ b/src/Migration.php @@ -126,6 +126,7 @@ public function getMigrationSql($version, $increment) return $file; } } + return null; } /** @@ -239,6 +240,23 @@ public function up($upVersion = null, $force = false) $this->migrate($upVersion, 1, $force); } + /** + * Run all scripts to up or down the database version from current up to latest version or the specified version. + * + * @param int $upVersion + * @param bool $force + */ + public function update($upVersion = null, $force = false) + { + $versionInfo = $this->getCurrentVersion(); + $version = intval($versionInfo['version']); + $increment = 1; + if ($upVersion !== null && $upVersion < $version) { + $increment = -1; + } + $this->migrate($upVersion, $increment, $force); + } + /** * Run all scripts to down the database version from current version up to the specified version. * From 77f7d51e71b12ae5767c360bcc4beb389bba265e Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Thu, 25 May 2017 08:13:49 -0300 Subject: [PATCH 09/12] Better error message in console. --- src/Console/ConsoleCommand.php | 9 +++++++ src/Console/DatabaseVersionCommand.php | 10 +++++--- src/Console/DownCommand.php | 34 ++++++++++++++------------ src/Console/InstallCommand.php | 34 ++++++++++++++------------ src/Console/ResetCommand.php | 26 ++++++++++++-------- src/Console/UpCommand.php | 34 ++++++++++++++------------ src/Console/UpdateCommand.php | 31 +++++++++++++---------- 7 files changed, 107 insertions(+), 71 deletions(-) diff --git a/src/Console/ConsoleCommand.php b/src/Console/ConsoleCommand.php index 1b88295..9498cda 100644 --- a/src/Console/ConsoleCommand.php +++ b/src/Console/ConsoleCommand.php @@ -89,5 +89,14 @@ protected function execute(InputInterface $input, OutputInterface $output) } } + protected function handleError($ex, OutputInterface $output) + { + $output->writeln('-- Error migrating tables --'); + if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { + $output->writeln(get_class($ex)); + $output->writeln($ex->getMessage()); + } + } + } diff --git a/src/Console/DatabaseVersionCommand.php b/src/Console/DatabaseVersionCommand.php index 9375c2b..74c14c9 100644 --- a/src/Console/DatabaseVersionCommand.php +++ b/src/Console/DatabaseVersionCommand.php @@ -25,8 +25,12 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { parent::execute($input, $output); - $versionInfo = $this->migration->getCurrentVersion(); - $output->writeln('version: ' . $versionInfo['version']); - $output->writeln('status.: ' . $versionInfo['status']); + try { + $versionInfo = $this->migration->getCurrentVersion(); + $output->writeln('version: ' . $versionInfo['version']); + $output->writeln('status.: ' . $versionInfo['status']); + } catch (\Exception $ex) { + $this->handleError($ex, $output); + } } } diff --git a/src/Console/DownCommand.php b/src/Console/DownCommand.php index 00bee71..0dbbead 100644 --- a/src/Console/DownCommand.php +++ b/src/Console/DownCommand.php @@ -25,23 +25,27 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { - $versionInfo = $this->migration->getCurrentVersion(); - if (strpos($versionInfo['status'], 'partial') !== false) { - $helper = $this->getHelper('question'); - $question = new ConfirmationQuestion( - 'The database was not fully updated and maybe be unstable. Did you really want migrate the version? (y/N)', - false - ); - - if (!$helper->ask($input, $output, $question)) { - $output->writeln('Aborted.'); - - return; + try { + $versionInfo = $this->migration->getCurrentVersion(); + if (strpos($versionInfo['status'], 'partial') !== false) { + $helper = $this->getHelper('question'); + $question = new ConfirmationQuestion( + 'The database was not fully updated and maybe be unstable. Did you really want migrate the version? (y/N)', + false + ); + + if (!$helper->ask($input, $output, $question)) { + $output->writeln('Aborted.'); + + return; + } } - } - parent::execute($input, $output); - $this->migration->down($this->upTo, true); + parent::execute($input, $output); + $this->migration->down($this->upTo, true); + } catch (\Exception $ex) { + $this->handleError($ex, $output); + } } } diff --git a/src/Console/InstallCommand.php b/src/Console/InstallCommand.php index 58aa3c4..be9159d 100644 --- a/src/Console/InstallCommand.php +++ b/src/Console/InstallCommand.php @@ -26,22 +26,26 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { - parent::execute($input, $output); - - $action = 'Database is already versioned. '; try { - $this->migration->getCurrentVersion(); - } catch (DatabaseNotVersionedException $ex) { - $action = 'Created the version table'; - $this->migration->createVersion(); - } catch (OldVersionSchemaException $ex) { - $action = 'Updated the version table'; - $this->migration->updateTableVersion(); - } + parent::execute($input, $output); - $version = $this->migration->getCurrentVersion(); - $output->writeln($action); - $output->writeln('current version: ' . $version['version']); - $output->writeln('current status.: ' . $version['status']); + $action = 'Database is already versioned. '; + try { + $this->migration->getCurrentVersion(); + } catch (DatabaseNotVersionedException $ex) { + $action = 'Created the version table'; + $this->migration->createVersion(); + } catch (OldVersionSchemaException $ex) { + $action = 'Updated the version table'; + $this->migration->updateTableVersion(); + } + + $version = $this->migration->getCurrentVersion(); + $output->writeln($action); + $output->writeln('current version: ' . $version['version']); + $output->writeln('current status.: ' . $version['status']); + } catch (\Exception $ex) { + $this->handleError($ex, $output); + } } } diff --git a/src/Console/ResetCommand.php b/src/Console/ResetCommand.php index 9ceea01..cbacae2 100644 --- a/src/Console/ResetCommand.php +++ b/src/Console/ResetCommand.php @@ -25,17 +25,23 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { - $helper = $this->getHelper('question'); - $question = new ConfirmationQuestion('This will ERASE all of data in your data. Continue with this action? (y/N) ', false); - - if (!$helper->ask($input, $output, $question)) { - $output->writeln('Aborted.'); - return; + try { + $helper = $this->getHelper('question'); + $question = new ConfirmationQuestion('This will ERASE all of data in your data. Continue with this action? (y/N) ', + false); + + if (!$helper->ask($input, $output, $question)) { + $output->writeln('Aborted.'); + + return; + } + + parent::execute($input, $output); + $this->migration->prepareEnvironment(); + $this->migration->reset($this->upTo); + } catch (\Exception $ex) { + $this->handleError($ex, $output); } - - parent::execute($input, $output); - $this->migration->prepareEnvironment(); - $this->migration->reset($this->upTo); } } diff --git a/src/Console/UpCommand.php b/src/Console/UpCommand.php index e1907e5..7a8a0a4 100644 --- a/src/Console/UpCommand.php +++ b/src/Console/UpCommand.php @@ -25,22 +25,26 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { - $versionInfo = $this->migration->getCurrentVersion(); - if (strpos($versionInfo['status'], 'partial') !== false) { - $helper = $this->getHelper('question'); - $question = new ConfirmationQuestion( - 'The database was not fully updated and maybe be unstable. Did you really want migrate the version? (y/N) ', - false - ); - - if (!$helper->ask($input, $output, $question)) { - $output->writeln('Aborted.'); - - return; + try { + $versionInfo = $this->migration->getCurrentVersion(); + if (strpos($versionInfo['status'], 'partial') !== false) { + $helper = $this->getHelper('question'); + $question = new ConfirmationQuestion( + 'The database was not fully updated and maybe be unstable. Did you really want migrate the version? (y/N) ', + false + ); + + if (!$helper->ask($input, $output, $question)) { + $output->writeln('Aborted.'); + + return; + } } - } - parent::execute($input, $output); - $this->migration->up($this->upTo, true); + parent::execute($input, $output); + $this->migration->up($this->upTo, true); + } catch (\Exception $ex) { + $this->handleError($ex, $output); + } } } diff --git a/src/Console/UpdateCommand.php b/src/Console/UpdateCommand.php index 859c8fd..a6c8f8c 100644 --- a/src/Console/UpdateCommand.php +++ b/src/Console/UpdateCommand.php @@ -27,21 +27,26 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output) { - $versionInfo = $this->migration->getCurrentVersion(); - if (strpos($versionInfo['status'], 'partial') !== false) { - $helper = $this->getHelper('question'); - $question = new ConfirmationQuestion( - 'The database was not fully updated and maybe be unstable. Did you really want migrate the version? (y/N) ', - false - ); + try { + $versionInfo = $this->migration->getCurrentVersion(); + if (strpos($versionInfo['status'], 'partial') !== false) { + $helper = $this->getHelper('question'); + $question = new ConfirmationQuestion( + 'The database was not fully updated and maybe be unstable. Did you really want migrate the version? (y/N) ', + false + ); + + if (!$helper->ask($input, $output, $question)) { + $output->writeln('Aborted.'); - if (!$helper->ask($input, $output, $question)) { - $output->writeln('Aborted.'); - return; + return; + } } - } - parent::execute($input, $output); - $this->migration->update($this->upTo, true); + parent::execute($input, $output); + $this->migration->update($this->upTo, true); + } catch (\Exception $ex) { + $this->handleError($ex, $output); + } } } From 7c44b514fc2be0e7ed3261f7132d9ecfac924e60 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Sat, 27 May 2017 16:35:05 -0300 Subject: [PATCH 10/12] Renamed classes --- .travis.yml | 2 +- README.md | 8 ++++---- phpunit.xml.dist | 2 +- .../AbstractDatabase.php} | 4 ++-- .../DatabaseInterface.php} | 4 ++-- .../DblibDatabase.php} | 4 ++-- .../MysqlDatabase.php} | 4 ++-- .../PgsqlDatabase.php} | 4 ++-- .../SqliteDatabase.php} | 4 ++-- src/Migration.php | 14 +++++++------- tests/{BaseCommand.php => BaseDatabase.php} | 2 +- ...{MysqlCommandTest.php => MysqlDatabaseTest.php} | 4 ++-- ...resCommandTest.php => PostgresDatabaseTest.php} | 4 ++-- ...erCommandTest.php => SqlServerDatabaseTest.php} | 4 ++-- ...qliteCommandTest.php => SqliteDatabaseTest.php} | 4 ++-- 15 files changed, 34 insertions(+), 34 deletions(-) rename src/{Commands/AbstractCommand.php => Database/AbstractDatabase.php} (95%) rename src/{Commands/CommandInterface.php => Database/DatabaseInterface.php} (85%) rename src/{Commands/DblibCommand.php => Database/DblibDatabase.php} (96%) rename src/{Commands/MysqlCommand.php => Database/MysqlDatabase.php} (93%) rename src/{Commands/PgsqlCommand.php => Database/PgsqlDatabase.php} (96%) rename src/{Commands/SqliteCommand.php => Database/SqliteDatabase.php} (92%) rename tests/{BaseCommand.php => BaseDatabase.php} (98%) rename tests/{MysqlCommandTest.php => MysqlDatabaseTest.php} (82%) rename tests/{PostgresCommandTest.php => PostgresDatabaseTest.php} (82%) rename tests/{SqlServerCommandTest.php => SqlServerDatabaseTest.php} (88%) rename tests/{SqliteCommandTest.php => SqliteDatabaseTest.php} (81%) diff --git a/.travis.yml b/.travis.yml index 7f83832..1f1772c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,5 +8,5 @@ install: - composer install script: - - phpunit tests/SqliteCommandTest.php + - phpunit tests/SqliteDatabaseTest.php diff --git a/README.md b/README.md index 5e448e6..c30abdf 100644 --- a/README.md +++ b/README.md @@ -134,8 +134,8 @@ This library has integrated tests and need to be setup for each database you wan Basiclly you have the follow tests: ``` -phpunit tests/SqliteCommandTest.php -phpunit tests/MysqlCommandTest.php -phpunit tests/PostgresCommandTest.php -phpunit tests/SqlServerCommandTest.php +phpunit tests/SqliteDatabaseTest.php +phpunit tests/MysqlDatabaseTest.php +phpunit tests/PostgresDatabaseTest.php +phpunit tests/SqlServerDatabaseTest.php ``` \ No newline at end of file diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 32c9fc6..40fcae7 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -22,7 +22,7 @@ and open the template in the editor. - ./tests/SqliteCommandTest.php + ./tests/SqliteDatabaseTest.php diff --git a/src/Commands/AbstractCommand.php b/src/Database/AbstractDatabase.php similarity index 95% rename from src/Commands/AbstractCommand.php rename to src/Database/AbstractDatabase.php index 2ad5896..5f8d415 100644 --- a/src/Commands/AbstractCommand.php +++ b/src/Database/AbstractDatabase.php @@ -1,12 +1,12 @@ _dbCommand)) { - $class = $this->getCommandClassName(); + $class = $this->getDatabaseClassName(); $this->_dbCommand = new $class($this->getDbDriver()); } return $this->_dbCommand; } - protected function getCommandClassName() + protected function getDatabaseClassName() { - return "\\ByJG\\DbMigration\\Commands\\" . ucfirst($this->uri->getScheme()) . "Command"; + return "\\ByJG\\DbMigration\\Database\\" . ucfirst($this->uri->getScheme()) . "Database"; } /** @@ -134,7 +134,7 @@ public function getMigrationSql($version, $increment) */ public function prepareEnvironment() { - $class = $this->getCommandClassName(); + $class = $this->getDatabaseClassName(); $class::prepareEnvironment($this->uri); } diff --git a/tests/BaseCommand.php b/tests/BaseDatabase.php similarity index 98% rename from tests/BaseCommand.php rename to tests/BaseDatabase.php index 31158cc..63136ec 100644 --- a/tests/BaseCommand.php +++ b/tests/BaseDatabase.php @@ -5,7 +5,7 @@ class_alias('\PHPUnit_Framework_TestCase', '\PHPUnit\Framework\TestCase'); } -abstract class BaseCommand extends \PHPUnit\Framework\TestCase +abstract class BaseDatabase extends \PHPUnit\Framework\TestCase { protected $uri = null; diff --git a/tests/MysqlCommandTest.php b/tests/MysqlDatabaseTest.php similarity index 82% rename from tests/MysqlCommandTest.php rename to tests/MysqlDatabaseTest.php index 765d0aa..2b11247 100644 --- a/tests/MysqlCommandTest.php +++ b/tests/MysqlDatabaseTest.php @@ -1,8 +1,8 @@ Date: Sat, 27 May 2017 16:38:23 -0300 Subject: [PATCH 11/12] Minor typo --- src/Console/ConsoleCommand.php | 5 ++++- src/Database/PgsqlDatabase.php | 4 ++++ src/Migration.php | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Console/ConsoleCommand.php b/src/Console/ConsoleCommand.php index 9498cda..740d557 100644 --- a/src/Console/ConsoleCommand.php +++ b/src/Console/ConsoleCommand.php @@ -2,7 +2,6 @@ namespace ByJG\DbMigration\Console; -use ByJG\AnyDataset\ConnectionManagement; use ByJG\DbMigration\Migration; use ByJG\Util\Uri; use Symfony\Component\Console\Command\Command; @@ -89,6 +88,10 @@ protected function execute(InputInterface $input, OutputInterface $output) } } + /** + * @param \Exception|\Error $ex + * @param \Symfony\Component\Console\Output\OutputInterface $output + */ protected function handleError($ex, OutputInterface $output) { $output->writeln('-- Error migrating tables --'); diff --git a/src/Database/PgsqlDatabase.php b/src/Database/PgsqlDatabase.php index 6b8d47b..ef372b9 100644 --- a/src/Database/PgsqlDatabase.php +++ b/src/Database/PgsqlDatabase.php @@ -21,6 +21,10 @@ protected static function getDbDriverWithoutDatabase(Uri $uri) return Factory::getDbRelationalInstance($customUri->withPath('/')->__toString()); } + /** + * @param \ByJG\AnyDataset\DbDriverInterface $dbDriver + * @param $database + */ protected static function createDatabaseIfNotExists($dbDriver, $database) { $currentDbName = $dbDriver->getScalar( diff --git a/src/Migration.php b/src/Migration.php index ced017c..b5e66db 100644 --- a/src/Migration.php +++ b/src/Migration.php @@ -174,7 +174,7 @@ public function updateTableVersion() */ public function getCurrentVersion() { - return $this->getDbCommand()->getVersion();; + return $this->getDbCommand()->getVersion(); } /** From e1dfd71252691ad80570a4e27f69befe9520dc09 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Sat, 27 May 2017 17:24:48 -0300 Subject: [PATCH 12/12] Update README.md --- README.md | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c30abdf..344443d 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![SensioLabsInsight](https://insight.sensiolabs.com/projects/571cb412-7018-4938-a4e5-0f9ce44956d7/mini.png)](https://insight.sensiolabs.com/projects/571cb412-7018-4938-a4e5-0f9ce44956d7) [![Build Status](https://travis-ci.org/byjg/migration.svg?branch=master)](https://travis-ci.org/byjg/migration) -A micro framework in PHP for managing a set of database migrations using pure Sql. +Simple library in PHP for database version control. Supports Sqlite, MySql, Sql Server and Postgres. Database Migration is a set of commands for upgrade or downgrade a database. This library uses only SQL commands. diff --git a/composer.json b/composer.json index ae07d2f..2656caa 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "byjg/migration", - "description": "A micro framework in PHP for managing a set of database migrations using pure Sql.", + "description": "Simple library in PHP for database version control. Supports Sqlite, MySql, Sql Server and Postgres.", "authors": [ { "name": "jg",