diff --git a/app/controllers/web_migrate.php b/app/controllers/web_migrate.php index d155ee6ec66489932bef48cf455ac43a7b488f6b..7b8def386b4edae395bc230593638e1fc7aeec6c 100644 --- a/app/controllers/web_migrate.php +++ b/app/controllers/web_migrate.php @@ -17,8 +17,11 @@ class WebMigrateController extends StudipController parent::before_filter($action, $args); + DBSchemaVersion::validateSchemaVersion(); + $this->target = Request::int('target'); - $this->version = new DBSchemaVersion('studip'); + $this->branch = Request::get('branch', '0'); + $this->version = new DBSchemaVersion('studip', $this->branch); $this->migrator = new Migrator( "{$GLOBALS['STUDIP_BASE_PATH']}/db/migrations", $this->version, @@ -45,9 +48,7 @@ class WebMigrateController extends StudipController $this->lock->lock(['timestamp' => time(), 'user_id' => $GLOBALS['user']->id]); - foreach (Request::getArray('versions') as $version) { - $this->migrator->execute($version, 'up'); - } + $this->migrator->migrateTo($this->target); $this->lock->release(); @@ -78,51 +79,32 @@ class WebMigrateController extends StudipController public function history_action() { - $this->history = array_diff_key( - $this->migrator->relevantMigrations(0), - $this->migrator->relevantMigrations(null) - ); - } - - public function revert_action() - { - ob_start(); - set_time_limit(0); - - $this->lock->lock(['timestamp' => time(), 'user_id' => $GLOBALS['user']->id]); - - foreach (Request::getArray('versions') as $version) { - $this->migrator->execute($version, 'down'); - } - - $this->lock->release(); - - $announcements = ob_get_clean(); - PageLayout::postSuccess( - _('Die Datenbank wurde erfolgreich migriert.'), - array_filter(explode("\n", $announcements)) - ); - - $_SESSION['migration-check'] = [ - 'timestamp' => time(), - 'count' => 0, - ]; - - $this->redirect('history'); + $this->migrations = $this->migrator->relevantMigrations(0); + $this->offset = -1; + $this->target = 0; + $this->render_action('index'); } public function setupSidebar($action) { $views = Sidebar::get()->addWidget(new ViewsWidget()); $views->addLink( - _('Offene Migrationen'), - $this->url_for('index') + _('Migrationen ausführen'), + $this->url_for('index', ['branch' => $this->branch]) )->setActive($action === 'index'); $views->addLink( - _('Ausgeführte Migrationen'), - $this->url_for('history') + _('Migrationen zurücknehmen'), + $this->url_for('history', ['branch' => $this->branch]) )->setActive($action === 'history'); + $widget = new SelectWidget(_('Branch'), $this->url_for($action), 'branch'); + Sidebar::get()->addWidget($widget); + + foreach ($this->version->getAllBranches() as $branch) { + $element = new SelectElement($branch, $branch ?: 'default', $branch == $this->branch); + $widget->addElement($element); + } + $widget = Sidebar::get()->addWidget(new SidebarWidget()); $widget->setTitle(_('Aktueller Versionsstand')); $widget->addElement(new WidgetElement($this->version->get())); diff --git a/app/views/web_migrate/history.php b/app/views/web_migrate/history.php deleted file mode 100644 index ed8b853c8dd38ea6203ca372fda55351e8df9d36..0000000000000000000000000000000000000000 --- a/app/views/web_migrate/history.php +++ /dev/null @@ -1,83 +0,0 @@ -<? if (STUDIP\ENV === 'development'): ?> -<form method="post" action="<?= $controller->link_for('revert') ?>"> - <?= CSRFProtection::tokenTag() ?> -<? endif; ?> - <table class="default" id="migration-list"> - <caption> - <? if (STUDIP\ENV === 'development'): ?> - <?= _('Die markierten Anpassungen werden beim Klick auf "Starten" zurückgesetzt:') ?> - <? else: ?> - <?= _('Diese Anpassungen wurden im System bereits ausgeführt.') ?> - <? endif; ?> - </caption> - <colgroup> - <? if (STUDIP\ENV === 'development' && !$lock->isLocked($lock_data)): ?> - <col style="width: 24px"> - <? endif; ?> - <col style="width: 120px"> - <col> - </colgroup> - <thead> - <tr> - <? if (STUDIP\ENV === 'development' && !$lock->isLocked($lock_data)): ?> - <th> - <input type="checkbox" - data-proxyfor="#migration-list tbody :checkbox" - data-activates="#migration-list tfoot .button"> - </th> - <? endif; ?> - <th><?= _('Nr.') ?></th> - <th><?= _('Beschreibung') ?></th> - </tr> - </thead> - <tbody> - <? foreach ($history as $number => $migration): ?> - <tr> - <? if (STUDIP\ENV === 'development' && !$lock->isLocked($lock_data)): ?> - <td> - <input type="checkbox" - name="versions[]" value="<?= htmlReady($number) ?>"> - </td> - <? endif; ?> - <td> - <?= htmlReady($number) ?> - </td> - <td> - <? if ($migration->description()): ?> - <?= htmlReady($migration->description()) ?> - <? else: ?> - <em><?= _('keine Beschreibung vorhanden') ?></em> - <? endif ?> - </td> - </tr> - <? endforeach; ?> - </tbody> - <? if (STUDIP\ENV === 'development'): ?> - <tfoot> - <tr> - <td colspan="3"> - <? if ($lock->isLocked($lock_data)): ?> - <?= MessageBox::info(sprintf( - _('Die Migration wurde %s von %s bereits angestossen und läuft noch.'), - reltime($lock_data['timestamp']), - htmlReady(User::find($lock_data['user_id'])->getFullName() - )), [ - sprintf( - _('Sollte während der Migration ein Fehler aufgetreten sein, so können Sie ' - . 'diese Sperre durch den unten stehenden Link oder das Löschen der Datei ' - . '<em>%s</em> auflösen.'), - $lock->getFilename() - ) - ]) ?> - <?= Studip\LinkButton::create(_('Sperre aufheben'), $controller->link_for('release', $target)) ?> - <? else: ?> - <?= Studip\Button::createAccept(_('Starten'), 'start')?> - <? endif; ?> - </td> - </tr> - </tfoot> - <? endif; ?> - </table> -<? if (STUDIP\ENV === 'development'): ?> -</form> -<? endif; ?> diff --git a/app/views/web_migrate/index.php b/app/views/web_migrate/index.php index c9b972595b8193c70be09e90d30d608026ea752d..6bba52fbf24ed5bc499e4fdf66d0f1bd5247cecf 100644 --- a/app/views/web_migrate/index.php +++ b/app/views/web_migrate/index.php @@ -3,54 +3,44 @@ <? else: ?> <form method="post" action="<?= $controller->link_for('migrate') ?>"> <?= CSRFProtection::tokenTag() ?> -<? if (isset($target)): ?> - <input type="hidden" name="target" value="<?= htmlReady($target) ?>"> -<? endif ?> -<? if (STUDIP\ENV !== 'development'): ?> - <?= addHiddenFields('versions', array_keys($migrations)) ?> -<? endif; ?> - + <? if (isset($target)): ?> + <input type="hidden" name="target" value="<?= htmlReady($target) ?>"> + <? endif ?> + <input type="hidden" name="branch" value="<?= htmlReady($branch) ?>"> <table class="default" id="migration-list"> <caption> - <? if (STUDIP\ENV === 'development'): ?> - <?= _('Die markierten Anpassungen werden beim Klick auf "Starten" ausgeführt:') ?> - <? else: ?> <?= _('Die hier aufgeführten Anpassungen werden beim Klick auf "Starten" ausgeführt:') ?> - <? endif; ?> </caption> <colgroup> - <? if (STUDIP\ENV === 'development' && !$lock->isLocked($lock_data)): ?> <col style="width: 24px"> - <? endif; ?> <col style="width: 120px"> <col> + <col> </colgroup> <thead> <tr> - <? if (STUDIP\ENV === 'development' && !$lock->isLocked($lock_data)): ?> - <th> - <input type="checkbox" - data-proxyfor="#migration-list tbody :checkbox" - data-activates="#migration-list tfoot .button"> - </th> - <? endif; ?> + <th></th> <th><?= _('Nr.') ?></th> + <th><?= _('Name') ?></th> <th><?= _('Beschreibung') ?></th> </tr> </thead> <tbody> <? foreach ($migrations as $number => $migration): ?> + <? $version = $migrator->migrationBranchAndVersion($number) ?> <tr> - <? if (STUDIP\ENV === 'development' && !$lock->isLocked($lock_data)): ?> <td> - <input type="checkbox" checked - name="versions[]" value="<?= htmlReady($number) ?>"> + <? if ($version[0] === $branch): ?> + <input type="radio" name="target" value="<?= $version[1] + $offset ?>"> + <? endif ?> </td> - <? endif; ?> <td> <?= htmlReady($number) ?> </td> + <td> + <?= htmlReady(get_class($migration)) ?> + </td> <td> <? if ($migration->description()): ?> <?= htmlReady($migration->description()) ?> @@ -63,7 +53,7 @@ </tbody> <tfoot> <tr> - <td colspan="<?= 2 + (int) (STUDIP\ENV === 'development') ?>"> + <td colspan="4"> <? if ($lock->isLocked($lock_data)): $user = User::find($lock_data['user_id']); ?> diff --git a/cli/migrate.php b/cli/migrate.php index 2b43b17b82e9d4d151407d32036cbcde77cc2a57..0fe6bc35c436d6c5fe44c37c1a926d1451147ad4 100755 --- a/cli/migrate.php +++ b/cli/migrate.php @@ -17,14 +17,14 @@ require_once __DIR__ . '/studip_cli_env.inc.php'; if (isset($_SERVER['argv'])) { # check for command line options - $options = getopt('1:d:lm:t:v'); + $options = getopt('b:d:lm:t:v'); if ($options === false) { exit(1); } # check for options - $single = false; $domain = 'studip'; + $branch = '0'; $list = false; $path = $STUDIP_BASE_PATH . '/db/migrations'; $verbose = false; @@ -32,8 +32,8 @@ if (isset($_SERVER['argv'])) { foreach ($options as $option => $value) { switch ($option) { - case '1': - $single = (string) $value; + case 'b': + $branch = (string) $value; break; case 'd': $domain = (string) $value; @@ -53,7 +53,9 @@ if (isset($_SERVER['argv'])) { } } - $version = new DBSchemaVersion($domain); + DBSchemaVersion::validateSchemaVersion(); + + $version = new DBSchemaVersion($domain, $branch); $migrator = new Migrator($path, $version, $verbose); if ($list) { @@ -61,15 +63,8 @@ if (isset($_SERVER['argv'])) { foreach ($migrations as $number => $migration) { $description = $migration->description() ?: '(no description)'; - printf("%3d %s\n", $number, $description); - } - } elseif ($single) { - $direction = 'up'; - if ($single[0] === '-') { - $direction = 'down'; - $single = substr($single, 1); + printf("%6s %-20s %s\n", $number, get_class($migration), $description); } - $migrator->execute($single, $direction); } else { $migrator->migrateTo($target); } diff --git a/cli/plugin_manager b/cli/plugin_manager index 9a065fe5bc4c610906d6c7a5e5e226f06590fce8..e566c75a13ef419f74a5b375debda62637c29d2c 100755 --- a/cli/plugin_manager +++ b/cli/plugin_manager @@ -132,18 +132,22 @@ if ($args) { // show usage if (!$pluginname) { - echo 'Usage: '. $args[0] .' migrate PLUGINNAME [-l] [-t] [-v]' . "\n"; + echo 'Usage: '. $args[0] .' migrate PLUGINNAME [-l] [-v] [-t target] [-b branch]' . "\n"; exit(1); } // parse options - list($errors, $options, $args) = getopts(array('l' => 'Ss l list', 'v' => 'Ss v verbose', 't'=> 'Vs t target')); + list($errors, $options, $args) = getopts(array('l' => 'Ss l list', 'v' => 'Ss v verbose', 't' => 'Vs t target', 'b' => 'Vs b branch')); $list = false; $verbose = false; $target = NULL; + $branch = '0'; foreach ($options as $option => $value) { switch ($option) { + case 'b': + $branch = ($value === false) ? '0' : $value; + break; case 'l': $list = $value; break; @@ -166,7 +170,7 @@ if ($args) { if (is_dir($plugindir . '/migrations')) { // if there are migrations, migrate - $schema_version = new DBSchemaVersion($plugin['name']); + $schema_version = new DBSchemaVersion($plugin['name'], $branch); $migrator = new Migrator($plugindir . '/migrations', $schema_version, $verbose); if ($list) { @@ -175,7 +179,7 @@ if ($args) { foreach ($migrations as $number => $migration) { $description = $migration->description() ?: '(no description)'; - printf("%3d %-20s %s\n", $number, get_class($migration), $description); + printf("%6s %-20s %s\n", $number, get_class($migration), $description); } } else { $migrator->migrateTo($target); @@ -214,7 +218,7 @@ if ($args) { if (is_dir($plugindir . '/migrations')) { $schema_version = new DBSchemaVersion($plugin['name']); $migrator = new Migrator($plugindir . '/migrations', $schema_version); - $migrator->migrate_to(0); + $migrator->migrateTo(0); } echo 'Das Plugin '. $plugin['name'] .' wurde ausgetragen.' . "\n"; diff --git a/db/migrations/03_step_87_extern_configurations.php b/db/migrations/03_step_87_extern_configurations.php index e4cf33bf553c55ca99f115d3a699bfa42a38d628..5b7b1f9d5b4d5155db2c4074601cbf4ffd7df616 100644 --- a/db/migrations/03_step_87_extern_configurations.php +++ b/db/migrations/03_step_87_extern_configurations.php @@ -1,18 +1,4 @@ <?php -$requirements = [ - "lib/functions.php", - $GLOBALS["RELATIVE_PATH_EXTERN"]."/extern_config.inc.php", - $GLOBALS["RELATIVE_PATH_EXTERN"]."/lib/ExternConfig.class.php", - $GLOBALS["RELATIVE_PATH_EXTERN"]."/lib/ExternConfigIni.class.php", - $GLOBALS["RELATIVE_PATH_EXTERN"]."/lib/ExternConfigDb.class.php", -]; - -foreach ($requirements as $file) { - if (!file_exists($file)) { - throw new Exception('Migration is not compatible with your Stud.IP version'); - } - require_once $file; -} class Step87ExternConfigurations extends Migration { diff --git a/db/migrations/18_step_00139_upload_file_reorg.php b/db/migrations/18_step_00139_upload_file_reorg.php index cb301540d6555b0f5cf5c74999368f61319ba724..51201e2a54e9f686ed775fd39780e640ff7c1327 100644 --- a/db/migrations/18_step_00139_upload_file_reorg.php +++ b/db/migrations/18_step_00139_upload_file_reorg.php @@ -1,14 +1,4 @@ <?php -$requirements = [ - __DIR__ . '/lib/datei.inc.php', -]; - -foreach ($requirements as $file) { - if (!file_exists($file)) { - throw new Exception('Migration is not compatible with your Stud.IP version'); - } - require_once $file; -} class Step00139UploadFileReorg extends Migration { diff --git a/db/migrations/202011031_add_missing_indices_resources.php b/db/migrations/202011031_add_missing_indices_resources.php index 063c60975e75e71cc46b5c8a878fc0384fca8012..a3da2c2bb26995e38406147c37103effd6ae19a5 100644 --- a/db/migrations/202011031_add_missing_indices_resources.php +++ b/db/migrations/202011031_add_missing_indices_resources.php @@ -12,6 +12,14 @@ class AddMissingIndicesResources extends Migration public function up() { + // avoid running this migration twice + $query = "SHOW INDEX FROM resource_temporary_permissions WHERE Key_name = 'user_id'"; + $result = DBManager::get()->query($query); + + if ($result && $result->rowCount() > 0) { + return; + } + $query = "ALTER TABLE `resource_temporary_permissions` ADD INDEX (`user_id`)"; DBManager::get()->exec($query); diff --git a/db/migrations/20201103_add_missing_indices.php b/db/migrations/20201103_add_missing_indices.php index 70124c919fac7745f09f5108f71080586557c784..b1b652bc53de0df53052f10e199202dd62dcbd31 100644 --- a/db/migrations/20201103_add_missing_indices.php +++ b/db/migrations/20201103_add_missing_indices.php @@ -13,6 +13,14 @@ class AddMissingIndices extends Migration public function up() { + // avoid running this migration twice + $query = "SHOW INDEX FROM activities WHERE Key_name = 'object_id'"; + $result = DBManager::get()->query($query); + + if ($result && $result->rowCount() > 0) { + return; + } + $query = "ALTER TABLE `activities` ADD INDEX `object_id` (`object_id`(32))"; DBManager::get()->exec($query); diff --git a/db/migrations/20201114_migration_history.php b/db/migrations/20201114_migration_history.php deleted file mode 100644 index 7d82a8c39cb932e014c852a5f4f4d37716f4bdb5..0000000000000000000000000000000000000000 --- a/db/migrations/20201114_migration_history.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php -class MigrationHistory extends Migration -{ - public function description() - { - return 'Extends migration table to allow a history of who did what when'; - } - - public function up() - { - $query = "ALTER TABLE `schema_versions` - ADD COLUMN `user_id` CHAR(32) CHARACTER SET latin1 COLLATE latin1_bin NULL DEFAULT NULL, - ADD COLUMN `mkdate` INT(11) UNSIGNED NULL DEFAULT NULL"; - DBManager::get()->exec($query); - } - - public function down() - { - $query = "ALTER TABLE `schema_versions` - DROP COLUMN `user_id`, - DROP COLUMN `mkdate`"; - DBManager::get()->exec($query); - } -} diff --git a/db/migrations/20210317_change_blubber_thread_following.php b/db/migrations/20210317_change_blubber_thread_following.php index 96098cdeb23868bc218f517d6c90414f993dd3ca..dadd953ec43d765b99808651a12fff9b458ed65d 100644 --- a/db/migrations/20210317_change_blubber_thread_following.php +++ b/db/migrations/20210317_change_blubber_thread_following.php @@ -8,6 +8,14 @@ class ChangeBlubberThreadFollowing extends Migration public function up() { + // avoid running this migration twice + $query = "SHOW TABLES LIKE 'blubber_threads_followstates'"; + $result = DBManager::get()->query($query); + + if ($result && $result->rowCount() > 0) { + return; + } + // Alter table to reflect new state $query = "RENAME TABLE `blubber_threads_unfollow` TO `blubber_threads_followstates`"; DBManager::get()->exec($query); diff --git a/db/migrations/20210322_migration_history_reworked.php b/db/migrations/20210322_migration_history_reworked.php index 4863e7276fc58ccb1447d886b92c95148099ebbd..4809b8de608438814f1dbaffb051e81639478817 100644 --- a/db/migrations/20210322_migration_history_reworked.php +++ b/db/migrations/20210322_migration_history_reworked.php @@ -3,17 +3,11 @@ class MigrationHistoryReworked extends Migration { public function description() { - return 'Reverts migration 20201114 and adds appropriate log actions'; + return 'Add log actions for migrations'; } public function up() { - // Drop columns - $query = "ALTER TABLE `schema_versions` - DROP COLUMN `user_id`, - DROP COLUMN `mkdate`"; - DBManager::get()->exec($query); - // Add log actions $query = "INSERT IGNORE INTO log_actions ( `action_id`, `name`, `description`, `info_template`, `active`, `expires`, `mkdate`, `chdate` @@ -46,11 +40,5 @@ class MigrationHistoryReworked extends Migration $statement = DBManager::get()->prepare($query); $statement->execute([':name' => 'MIGRATE_UP']); $statement->execute([':name' => 'MIGRATE_DOWN']); - - // Add columns - $query = "ALTER TABLE `schema_versions` - ADD COLUMN `user_id` CHAR(32) CHARACTER SET latin1 COLLATE latin1_bin NULL DEFAULT NULL, - ADD COLUMN `mkdate` INT(11) UNSIGNED NULL DEFAULT NULL"; - DBManager::get()->exec($query); } } diff --git a/db/migrations/259_migrations_reloaded.php b/db/migrations/259_migrations_reloaded.php index 722c7e243918d680072a297578e303153406af25..b73de5c327922d7003c10269e8f884f31b29fcec 100644 --- a/db/migrations/259_migrations_reloaded.php +++ b/db/migrations/259_migrations_reloaded.php @@ -1,58 +1,35 @@ <?php + class MigrationsReloaded extends Migration { public function description() { - return 'switch from a single successive migration version number to ' - . 'a collection of already executed migrations'; + return 'add branch column to schema_version'; } public function up() { - $query = "CREATE TABLE IF NOT EXISTS `schema_versions` ( - `domain` VARCHAR(255) COLLATE latin1_bin NOT NULL, - `version` BIGINT(20) UNSIGNED NOT NULL, - PRIMARY KEY `domain` (`domain`, `version`) - ) ENGINE=InnoDB ROW_FORMAT=DYNAMIC"; - DBManager::get()->exec($query); - - $query = "SELECT `domain`, `version` - FROM `schema_version` - WHERE `version` > 0"; - $rows = DBManager::get()->query($query)->fetchAll(PDO::FETCH_NUM); - - $query = "INSERT INTO `schema_versions` - VALUES (:domain, :version)"; - $statement = DBManager::get()->prepare($query); - foreach ($rows as list($domain, $version)) { - $statement->bindValue(':domain', $domain); - - for ($i = 1; $i <= $version; $i += 1) { - $statement->bindValue(':version', $i); - $statement->execute(); - } - } - - $query = "DROP TABLE IF EXISTS `schema_version`"; - DBManager::get()->exec($query); + $db = DBManager::get(); + + $sql = "ALTER TABLE schema_version + CHANGE domain domain VARCHAR(255) COLLATE latin1_bin NOT NULL, + ADD branch VARCHAR(64) COLLATE latin1_bin NOT NULL DEFAULT '0' AFTER domain, + DROP PRIMARY KEY, + ADD PRIMARY KEY (domain, branch)"; + $db->exec($sql); } public function down() { - $query = "CREATE TABLE IF NOT EXISTS `schema_version` ( - `domain` VARCHAR(255) COLLATE latin1_bin NOT NULL, - `version` INT(11) UNSIGNED NOT NULL, - PRIMARY KEY (`domain`) - ) ENGINE=InnoDB ROW_FORMAT=DYNAMIC"; - DBManager::get()->exec($query); + $db = DBManager::get(); - $query = "INSERT IGNORE INTO `schema_version` - SELECT `domain`, MAX(`version`) - FROM `schema_versions` - GROUP BY `domain`"; - DBManager::get()->exec($query); + $sql = "DELETE FROM schema_version WHERE branch != '0'"; + $db->exec($sql); - $query = "DROP TABLE IF EXISTS `schema_versions`"; - DBManager::get()->exec($query); + $sql = 'ALTER TABLE schema_version + DROP PRIMARY KEY, + ADD PRIMARY KEY (domain), + DROP branch'; + $db->exec($sql); } -}; +} diff --git a/lib/classes/PluginAdministration.php b/lib/classes/PluginAdministration.php index 8a3dda5bc5e1155192323aca2fd270b8dbe7162c..f42f594794e2e667db8dff8a612de89946a5d0e4 100644 --- a/lib/classes/PluginAdministration.php +++ b/lib/classes/PluginAdministration.php @@ -251,7 +251,8 @@ class PluginAdministration if (is_dir($new_pluginpath . '/migrations')) { $migrator = new Migrator($new_pluginpath . '/migrations', $schema_version); - $new_version = $migrator->topVersion(); + $all_branches = array_fill_keys($schema_version->getAllBranches(), 0); + $new_version = $migrator->topVersion(true) + $all_branches; } $migrator = new Migrator($plugindir . '/migrations', $schema_version); diff --git a/lib/classes/StudipCacheFactory.class.php b/lib/classes/StudipCacheFactory.class.php index 805aa9d8be8ae3c41bda9849f89766ad2050fbb0..cc7be15dd434a5adfc6c731f6b5d36f5b4da77b0 100644 --- a/lib/classes/StudipCacheFactory.class.php +++ b/lib/classes/StudipCacheFactory.class.php @@ -189,7 +189,7 @@ class StudipCacheFactory # default class if (is_null($cache_class)) { $version = new DBSchemaVersion(); - if (!$version->contains(224)) { + if ($version->get() < 224) { // db cache is not yet available, use StudipMemoryCache return 'StudipMemoryCache'; } diff --git a/lib/migrations/DBSchemaVersion.php b/lib/migrations/DBSchemaVersion.php index 797a1c8b5e451519c75c9aa289471abc57d53b79..207a804aee7744322c3192440cce51d3141befac 100644 --- a/lib/migrations/DBSchemaVersion.php +++ b/lib/migrations/DBSchemaVersion.php @@ -6,8 +6,8 @@ * * @author Elmar Ludwig * @copyright 2007 Elmar Ludwig - * @license GPL2 or any later version - * @package migrations + * @license GPL2 or any later version + * @package migrations */ class DBSchemaVersion implements SchemaVersion { @@ -19,26 +19,32 @@ class DBSchemaVersion implements SchemaVersion private $domain; /** - * schema versions + * branch of schema version * - * @var array + * @var string */ - private $versions = []; + private $branch; /** + * current schema version numbers + * + * @access private * @var array */ - private $data = []; + private $versions; /** * Initialize a new DBSchemaVersion for a given domain. * The default domain name is 'studip'. * * @param string $domain domain name (optional) + * @param string $branch schema branch (optional) */ - public function __construct($domain = 'studip') + public function __construct($domain = 'studip', $branch = 0) { $this->domain = $domain; + $this->branch = $branch; + $this->versions = [0]; $this->initSchemaInfo(); } @@ -53,132 +59,134 @@ class DBSchemaVersion implements SchemaVersion } /** - * Initialize the current schema version. + * Retrieve the branch of this schema. + * + * @return string schema branch */ - private function initSchemaInfo() + public function getBranch() { - $this->data = []; - - try { - $query = "SELECT version FROM schema_versions WHERE domain = ?"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$this->domain]); - $this->versions = $statement->fetchAll(PDO::FETCH_COLUMN); - } catch (PDOException $e) { - $query = "SELECT version FROM schema_version WHERE domain = ?"; - $statement = DBManager::get()->prepare($query); - $statement->execute([$this->domain]); - $this->versions = range(1, $statement->fetchColumn()); - } + return $this->branch; } /** - * Retrieve the current schema version. + * Retrieve all branches of this schema. * - * @return int schema version + * @return array all schema branches */ - public function get() + public function getAllBranches() { - return $this->versions ? max($this->versions) : 0; + return array_keys($this->versions); } /** - * Returns whether the given version is already present for the given - * domain. - * - * @param int $version Version number - * @return bool + * Check whether the current schema_version supports branches. */ - public function contains($version) + private function branchSupported() { - return in_array($version, $this->versions); + $result = DBManager::get()->query("DESCRIBE schema_version 'branch'"); + return $result && $result->rowCount() > 0; } /** - * Set the current schema version. - * - * @param int $version new schema version + * Initialize the current schema versions. */ - public function add($version) + private function initSchemaInfo() { - $version = (int) $version; - - try { - $query = "INSERT INTO `schema_versions` (`domain`, `version`) - VALUES (?, ?)"; - DBManager::get()->execute($query, [ - $this->domain, - $version, - ]); + if (!$this->branchSupported()) { + $query = "SELECT 0, version FROM schema_version WHERE domain = ?"; + } else { + $query = "SELECT branch, version FROM schema_version WHERE domain = ? ORDER BY branch"; + } + $statement = DBManager::get()->prepare($query); + $statement->execute([$this->domain]); + $versions = $statement->fetchAll(PDO::FETCH_COLUMN | PDO::FETCH_GROUP | PDO::FETCH_UNIQUE); - StudipLog::log( - 'MIGRATE_UP', - $version, - $this->domain - ); - } catch (PDOException $e) { - $query = "UPDATE `schema_version` - SET `version` = ? - WHERE `domain` = ?";; - DBManager::get()->execute($query, [ - $version, - $this->domain, - ]); + if ($versions) { + $this->versions = array_map('intval', $versions); } - NotificationCenter::postNotification( - 'SchemaVersionDidUpdate', - $this->domain, - $version - ); } /** - * Removes a schema version. + * Retrieve the current schema version. + * + * @param string $branch schema branch (optional) + * @return int schema version + */ + public function get($branch = 0) + { + return $this->versions[$branch ?: $this->branch]; + } + + /** + * Set the current schema version. * - * @param int $version schema version to remove + * @param int $version new schema version + * @param string $branch schema branch (optional) */ - public function remove($version) + public function set($version, $branch = 0) { - $version = (int) $version; + $this->versions[$branch ?: $this->branch] = (int) $version; - try { - $query = "DELETE FROM `schema_versions` - WHERE `domain` = ? AND `version` = ?"; - DBManager::get()->execute($query, [ + if (!$this->branchSupported()) { + $query = "INSERT INTO schema_version (domain, version) + VALUES (?, ?) + ON DUPLICATE KEY UPDATE version = VALUES(version)"; + $statement = DBManager::get()->prepare($query); + $statement->execute([ $this->domain, $version ]); - - StudipLog::log( - 'MIGRATE_DOWN', - $version, - $this->domain - ); - } catch (PDOException $e) { - $query = "UPDATE `schema_version` - SET `version` = ? - WHERE `domain` = ?"; - DBManager::get()->execute($query, [ - $version, + } else { + $query = "INSERT INTO schema_version (domain, branch, version) + VALUES (?, ?, ?) + ON DUPLICATE KEY UPDATE version = VALUES(version)"; + $statement = DBManager::get()->prepare($query); + $statement->execute([ $this->domain, + $branch ?: $this->branch, + $version ]); } NotificationCenter::postNotification( - 'SchemaVersionDidDelete', + 'SchemaVersionDidUpdate', $this->domain, $version ); } /** - * @param $domain - * @param $version - * @return string + * Validate correct structure of schema_version table. */ - static public function exists($domain, $version) + public static function validateSchemaVersion() { - return (bool)DBManager::get()->fetchColumn( - "SELECT 1 FROM schema_versions WHERE `domain` = ? AND `version` = ?", - [$domain, $version]); + $db = DBManager::get(); + $result = $db->query("SHOW TABLES LIKE 'schema_versions'"); + + if ($result && $result->rowCount() > 0) { + $backported_migrations = [ + 20200306, 20200306, 20200713, 20200811, 20200909, + 20200910, 20201002, 20201103, 202011031, 20210317 + ]; + + $query = "DELETE FROM schema_versions + WHERE domain = 'studip' AND version in (?)"; + $db->execute($query, [$backported_migrations]); + + $query = "CREATE TABLE schema_version ( + domain VARCHAR(255) COLLATE latin1_bin NOT NULL, + branch VARCHAR(64) COLLATE latin1_bin NOT NULL DEFAULT '0', + version INT(11) UNSIGNED NOT NULL, + PRIMARY KEY (domain, branch) + ) ENGINE=InnoDB ROW_FORMAT=DYNAMIC"; + $db->exec($query); + + $query = "INSERT INTO schema_version + SELECT domain, '0', MAX(version) FROM schema_versions + GROUP BY domain"; + $db->exec($query); + + $query = "DROP TABLE schema_versions"; + $db->exec($query); + } } } diff --git a/lib/migrations/Migration.php b/lib/migrations/Migration.php index 355bfe2dee502d2acfe32145468886f72721ccf6..d989d5341e36eceec6cd1d406f302d15419a3662 100644 --- a/lib/migrations/Migration.php +++ b/lib/migrations/Migration.php @@ -100,12 +100,28 @@ abstract class Migration * * @param string $format,... printf-style format string and parameters */ - protected function announce($format /* , ... */) + public function announce($format /* , ... */) { # format message $args = func_get_args(); $message = vsprintf(array_shift($args), $args); - return $this->write(Migrator::mark($message)); + return $this->write($this->mark($message)); } + + /** + * Pads and highlights a given text to a specific length with the given + * sign. + * + * @param string $text + * @param string $sign + */ + protected function mark($text, $sign = '=') + { + $text = trim($text); + if ($text) { + $text = " {$text} "; + } + return str_pad("{$sign}{$sign}{$text}", 79, $sign, STR_PAD_RIGHT); + } } diff --git a/lib/migrations/Migrator.php b/lib/migrations/Migrator.php index 92ec808c8dee567284eb18ba7390c3b5c5aabc91..eb0193a08dabec4177f67b6013b03d94e399eab3 100644 --- a/lib/migrations/Migrator.php +++ b/lib/migrations/Migrator.php @@ -38,16 +38,14 @@ * * (\d+)_([a-z_]+).php // (index)_(name).php * - * 20180524110400_my_first_migration.php - * 20180812152300_another_migration.php - * 20181110100900_and_one_last.php + * 001_my_first_migration.php + * 002_another_migration.php + * 003_and_one_last.php * - * Those numbers are used to order your migrations. Use the current time to - * define the chronological order of migrations. Gaps are allowed. In previous - * versions of the migration system, the numbers were naturally ordered starting - * with 1 but that proved to be rather unflexible regarding bug fixes that - * needed a migration to be executed. Thus, every executed migration number is - * stored and you may add a migration lateron between two other migrations. + * Those numbers are used to order your migrations. The first migration has + * to be a 1 (but you can use leading 0). Every following migration has to be + * the successor to the previous migration. No gaps are allowed. Just use + * natural numbers starting with 1. * * When migrating those numbers are used to determine the migrations needed to * fulfill the target version. @@ -102,7 +100,7 @@ * $migrator = new Migrator($path, $version, $verbose); * * # now migrate to target version - * $migrator->migrateTo(20181128100139); + * $migrator->migrateTo(5); * * If you want to migrate to the highest migration, you can just use NULL as * parameter: @@ -116,8 +114,6 @@ */ class Migrator { - const FILE_REGEXP = '/\b(\d+)([_-][_a-z0-9]+)+\.php$/'; - /** * Direction of migration, either "up" or "down" * @@ -135,9 +131,9 @@ class Migrator /** * Specifies the target version, may be NULL (alias for "highest migration") * - * @var int + * @var array */ - private $target_version; + private $target_versions; /** * How verbose shall the migrator be? @@ -192,8 +188,8 @@ class Migrator * the current schema version (provided by the SchemaVersion object) and a * target version calling the methods #up and #down in sequence. * - * @param mixed the target version as an integer or NULL thus migrating to - * the top migration + * @param mixed the target version as an integer, array or NULL thus + * migrating to the top migrations */ public function migrateTo($target_version) { @@ -201,83 +197,87 @@ class Migrator # you're on the right version if (empty($migrations)) { - $this->log("You are already at %d.\n", $this->schema_version->get()); + $this->log('You are already at %d.', $this->schema_version->get()); return; } $this->log( - "Currently at version %d. Now migrating %s to %d.\n", + 'Currently at version %d. Now migrating %s to %d.', $this->schema_version->get(), $this->direction, - $this->target_version + max($this->target_versions) ); - foreach ($migrations as $version => $migration) { - $this->execute($version, $this->direction, $migration); + foreach ($migrations as $number => $migration) { + list($branch, $version) = $this->migrationBranchAndVersion($number); + + $action = $this->isUp() ? 'Migrating' : 'Reverting'; + $migration->announce("{$action} %s", $number); + + if ($migration->description()) { + $this->log($migration->description()); + $this->log(str_repeat('-', 79)); + } + + $time_start = microtime(true); + $migration->migrate($this->direction); + + $action = $this->isUp() ? 'Migrated' : 'Reverted'; + $this->log(''); + $migration->announce("{$action} in %ss", round(microtime(true) - $time_start, 3)); + $this->log(''); + + $this->schema_version->set($this->isDown() ? $version - 1 : $version, $branch); + + $action = $this->isUp() ? 'MIGRATE_UP' : 'MIGRATE_DOWN'; + StudipLog::log($action, $number, $this->schema_version->getDomain()); } } /** - * Executes a migration's up or down method + * Calculate the selected target versions for all relevant branches. If a + * single branch is selected for migration, only that branch and all its + * children are considered relevant. * - * @param string $version Version to execute - * @param string $direction Up or down - * @param Migration $migration Migration to execute (optional, will be - * loaded if missing) + * @param mixed the target version as an integer, array or NULL thus + * migrating to the top migrations + * + * @return array an associative array, whose keys are the branch names + * and whose values are the target versions */ - public function execute($version, $direction, Migration $migration = null) + public function targetVersions($target_version) { - if ($this->isUp($direction) && $this->schema_version->contains($version)) { - $this->log("Version {$version} is already present.\n"); - return; + $top_versions = $this->topVersion(true); + $target_branch = $this->schema_version->getBranch(); + + if (is_array($target_version)) { + return $target_version; } - - if ($this->isDown($direction) && !$this->schema_version->contains($version)) { - $this->log("Version {$version} is not present.\n"); - return; - } - - if ($migration === null) { - $migrations = $this->migrationClasses(); - if (!isset($migrations[$version])) { - throw new Exception("Version {$version} is invalid"); + + $max_version = $target_branch ? $target_branch . '.' . $target_version : $target_version; + + foreach ($top_versions as $branch => $version) { + if ($branch == $target_branch) { + if (isset($target_version)) { + $top_versions[$branch] = $target_version; + } + } else if ($target_branch && strpos($branch, $target_branch . '.') !== 0) { + unset($top_versions[$branch]); + } else if (isset($target_version) && version_compare($branch, $max_version) >= 0) { + $top_versions[$branch] = 0; } - list($file, $class) = $migrations[$version]; - $migration = $this->loadMigration($file, $class); - } - - $action = $this->isUp($direction) ? 'Migrating' : 'Reverting'; - - $this->announce("{$action} %d", $version); - if ($migration->description()) { - $this->log($migration->description()); - $this->log(self::mark('', '-')); - } - - $time_start = microtime(true); - $migration->migrate($direction); - - $action = $this->isUp($direction) ? 'Migrated' : 'Reverted'; - $this->log(''); - $this->announce("{$action} in %ss", round(microtime(true) - $time_start, 3)); - $this->log(''); - - // Update schema version - if ($this->isDown($direction)) { - $this->schema_version->remove($version); - } else { - $this->schema_version->add($version); } - + + return $top_versions; } - + /** * Invoking this method will return a list of migrations with an index between * the current schema version (provided by the SchemaVersion object) and a * target version calling the methods #up and #down in sequence. * - * @param mixed the target version as an integer or NULL thus migrating to - * the top migration + * @param mixed the target version as an integer, array or NULL thus + * migrating to the top migrations * * @return array an associative array, whose keys are the migration's * version and whose values are the migration objects @@ -287,35 +287,29 @@ class Migrator // Load migrations $migrations = $this->migrationClasses(); - // Determine correct target version - $this->target_version = $target_version === null - ? $this->topVersion() - : (int) $target_version; - - // Determine max version (might differ from max schema version in db in - // development systems) - $max_version = min($this->topVersion(), $this->schema_version->get()); + // Determine correct target versions + $this->target_versions = $this->targetVersions($target_version); // Determine migration direction - if ($this->target_version > 0 && $this->target_version >= $max_version) { - $this->direction = 'up'; - } else { - $this->direction = 'down'; + foreach ($this->target_versions as $branch => $version) { + if ($this->schema_version->get($branch) < $version) { + $this->direction = 'up'; + break; + } else if ($version < $this->schema_version->get($branch)) { + $this->direction = 'down'; + break; + } } // Sort migrations in correct order - uksort($migrations, function ($a, $b) { - if (mb_strlen($a) > 8 && mb_strlen($b) > 8) { - return $a - $b; - } - return mb_substr($a, 0, 8) - mb_substr($b, 0, 8); - }); + uksort($migrations, 'version_compare'); if (!$this->isUp()) { $migrations = array_reverse($migrations, true); } $result = []; + foreach ($migrations as $version => $migration_file_and_class) { if (!$this->relevantMigration($version)) { continue; @@ -323,10 +317,15 @@ class Migrator list($file, $class) = $migration_file_and_class; - try { - $result[$version] = $this->loadMigration($file, $class); - } catch (Exception $e) { + $migration = require_once $file; + + if (!$migration instanceof Migration) { + $migration = new $class($this->verbose); + } else { + $migration->setVerbose($this->verbose); } + + $result[$version] = $migration; } return $result; @@ -342,47 +341,30 @@ class Migrator */ private function relevantMigration($version) { - if ($this->isUp()) { - return !$this->schema_version->contains($version) - && $version <= $this->target_version; - } elseif ($this->isDown()) { - return $this->schema_version->contains($version) - && $version > $this->target_version; + list($branch, $version) = $this->migrationBranchAndVersion($version); + $current_version = $this->schema_version->get($branch); + + if (!isset($this->target_versions[$branch])) { + return false; + } else if ($this->isUp()) { + return $current_version < $version + && $version <= $this->target_versions[$branch]; + } else if ($this->isDown()) { + return $current_version >= $version + && $version > $this->target_versions[$branch]; } return false; } - /** - * Loads a migration from the given file and creates and instance of it. - * - * @param string $file File name of migration to load - * @param string $class Class name to expect to be loaded from the file - * @return Migration instance - */ - private function loadMigration($file, $class) - { - if (class_exists($class)) { - $migration = new $class($this->verbose); - } else { - $migration = require $file; - if (!$migration instanceof Migration) { - $migration = new $class($this->verbose); - } else { - $migration->setVerbose($this->verbose); - } - } - return $migration; - } - /** * Am I migrating up? * * @return bool TRUE if migrating up, FALSE otherwise */ - private function isUp($direction = null) + private function isUp() { - return ($direction ?: $this->direction) === 'up'; + return $this->direction === 'up'; } /** @@ -390,9 +372,9 @@ class Migrator * * @return bool TRUE if migrating down, FALSE otherwise */ - private function isDown($direction = null) + private function isDown() { - return ($direction ?: $this->direction) === 'down'; + return $this->direction === 'down'; } /** @@ -433,10 +415,7 @@ class Migrator */ protected function migrationFiles() { - $files = glob($this->migrations_path . '/*.php'); - $files = array_filter($files, function ($file) { - return preg_match(self::FILE_REGEXP, $file); - }); + $files = glob($this->migrations_path . '/[0-9]*_*.php'); return $files; } @@ -450,19 +429,43 @@ class Migrator protected function migrationVersionAndName($migration_file) { $matches = []; - preg_match(self::FILE_REGEXP, $migration_file, $matches); - return [(int) $matches[1], $matches[2]]; + preg_match('/\b([0-9.]+)_([_a-z0-9]*)\.php$/', $migration_file, $matches); + return [$matches[1], $matches[2]]; + } + + /** + * Split a migration version into its branch and version parts. + * + * @param string a migration version + * @return array an array of two elements containing the migration's branch + * and version on this branch. + */ + public function migrationBranchAndVersion($version) + { + if (preg_match('/^(.*)\.([0-9]+)$/', $version, $matches)) { + $branch = preg_replace('/\b0+/', '', $matches[1]); + $version = (int) $matches[2]; + } else { + $branch = '0'; + $version = (int) $version; + } + return [$branch, $version]; } /** * Returns the top migration's version. * + * @param bool return top version for all branches, not just default one * @return int the top migration's version. */ - public function topVersion() + public function topVersion($all_branches = false) { - $versions = array_keys($this->migrationClasses()); - return $versions ? max($versions) : 0; + $versions = [0]; + foreach (array_keys($this->migrationClasses()) as $version) { + list($branch, $version) = $this->migrationBranchAndVersion($version); + $versions[$branch] = max($versions[$branch], $version); + } + return $all_branches ? $versions : $versions[$this->schema_version->getBranch()]; } /** @@ -479,39 +482,6 @@ class Migrator } $args = func_get_args(); - vprintf(trim(array_shift($args)) . "\n", $args); - } - - - /** - * Overridable method used to return a textual representation of a stronger - * ouput of what's going on in me. You can use me as you would use printf. - * - * @param string $format just a dummy value, instead use this method as you - * would use printf & co. - */ - protected function announce($format) - { - # format message - $args = func_get_args(); - $message = vsprintf(array_shift($args), $args); - - return $this->log(self::mark($message)); - } - - /** - * Pads and highlights a given text to a specific length with the given - * sign. - * - * @param string $text - * @param string $sign - */ - public static function mark($text, $sign = '=') - { - $text = trim($text); - if ($text) { - $text = " {$text} "; - } - return str_pad("{$sign}{$sign}{$text}", 79, $sign, STR_PAD_RIGHT); + vprintf(array_shift($args) . "\n", $args); } } diff --git a/lib/migrations/SchemaVersion.php b/lib/migrations/SchemaVersion.php index bbb2a6b26a7337dc320ae21c2e818dea4844b8fe..d2cf7662d12a1cf4706aafe9a3295d571e6d80cd 100644 --- a/lib/migrations/SchemaVersion.php +++ b/lib/migrations/SchemaVersion.php @@ -14,32 +14,32 @@ interface SchemaVersion { /** - * Returns current schema version (as maximum number). + * Retrieve the branch of this schema. * - * @return int schema version + * @return string schema branch */ - public function get(); + public function getBranch(); /** - * Returns whether the given version is already present for the given - * domain. + * Retrieve all branches of this schema. * - * @param int $version Version number - * @return bool + * @return array all schema branches */ - public function contains($version); + public function getAllBranches(); /** - * Adds a schema version. + * Returns current schema version. * - * @param int $version schema version + * @param string $branch schema branch (optional) + * @return int schema version */ - public function add($version); + public function get($branch = 0); /** - * Removes a schema version. + * Sets the new schema version. * - * @param int $version schema version + * @param int $version new schema version + * @param string $branch schema branch (optional) */ - public function remove($version); + public function set($version, $branch = 0); } diff --git a/lib/models/StudipNews.class.php b/lib/models/StudipNews.class.php index d164a4a0694d2c276850fcd8efcca5c22220477d..b528604bfbf8b27ca374ef0aaca1686e54cb5498 100644 --- a/lib/models/StudipNews.class.php +++ b/lib/models/StudipNews.class.php @@ -109,9 +109,6 @@ class StudipNews extends SimpleORMap implements PrivacyObject public static function CountUnread($range_id = 'studip', $user_id = false) { - if (!DBSchemaVersion::exists('studip', '20210201')) { - return 0; - } $query = "SELECT SUM(nw.chdate > IFNULL(b.visitdate, :threshold) AND nw.user_id != :user_id) FROM news_range a LEFT JOIN news nw ON (a.news_id = nw.news_id AND UNIX_TIMESTAMP() BETWEEN date AND date + expire) diff --git a/lib/plugins/engine/PluginManager.class.php b/lib/plugins/engine/PluginManager.class.php index 13973592e0b4c83031249f5e17bbfb71facee4dc..5f67c40aad264653e56b0113d2ea6cf81d88bf92 100644 --- a/lib/plugins/engine/PluginManager.class.php +++ b/lib/plugins/engine/PluginManager.class.php @@ -192,10 +192,6 @@ class PluginManager */ public function isPluginActivated ($id, $context) { - if (!DBSchemaVersion::exists('studip', '20210201')) { - return null; - } - if (!$context) { return null; } diff --git a/tests/unit/lib/classes/test-migrations/10_test_migration_ten.php b/tests/_data/migrations/10_test_migration_ten.php similarity index 100% rename from tests/unit/lib/classes/test-migrations/10_test_migration_ten.php rename to tests/_data/migrations/10_test_migration_ten.php diff --git a/tests/unit/lib/classes/test-migrations/1_test_migration_one.php b/tests/_data/migrations/1_test_migration_one.php similarity index 100% rename from tests/unit/lib/classes/test-migrations/1_test_migration_one.php rename to tests/_data/migrations/1_test_migration_one.php diff --git a/tests/_data/migrations/2.1_test_migration_two_one.php b/tests/_data/migrations/2.1_test_migration_two_one.php new file mode 100644 index 0000000000000000000000000000000000000000..13126d41ec0dd7ccd06182de7fdfc4b334a8b812 --- /dev/null +++ b/tests/_data/migrations/2.1_test_migration_two_one.php @@ -0,0 +1,4 @@ +<?php +class TestMigrationTwoOne extends Migration +{ +} diff --git a/tests/unit/lib/classes/test-migrations/2_test_migration_two.php b/tests/_data/migrations/2_test_migration_two.php similarity index 100% rename from tests/unit/lib/classes/test-migrations/2_test_migration_two.php rename to tests/_data/migrations/2_test_migration_two.php diff --git a/tests/unit/lib/classes/MigrationTest.php b/tests/unit/lib/classes/MigrationTest.php index 7085c9ee9d7f09456bc9f3192412029e36f116c2..1f0bc98c56b320f52af4499d411cb1c6f9c34f06 100644 --- a/tests/unit/lib/classes/MigrationTest.php +++ b/tests/unit/lib/classes/MigrationTest.php @@ -13,9 +13,7 @@ class MigrationTest extends \Codeception\Test\Unit public function setUp(): void { - $this->before = isset($GLOBALS['CACHING_ENABLE']) - ? $GLOBALS['CACHING_ENABLE'] - : null; + $this->before = $GLOBALS['CACHING_ENABLE'] ?? null; $GLOBALS['CACHING_ENABLE'] = false; require_once 'lib/classes/StudipCache.class.php'; @@ -41,30 +39,26 @@ class MigrationTest extends \Codeception\Test\Unit { return new class() implements SchemaVersion { - private $versions = []; + private $versions = [0]; - public function get() + public function getBranch() { - return count($this->versions) > 0 ? max($this->versions) : 0; + return 0; } - public function contains($version) + public function getAllBranches() { - return in_array($version, $this->versions); + return array_keys($this->versions); } - public function add($version) + public function get($branch = 0) { - if (!$this->contains($version)) { - $this->versions[] = $version; - } + return $this->versions[$branch]; } - public function remove($version) + public function set($version, $branch = 0) { - if ($this->contains($version)) { - $this->versions = array_diff($this->versions, [$version]); - } + $this->versions[$branch] = (int) $version; } }; } @@ -72,29 +66,11 @@ class MigrationTest extends \Codeception\Test\Unit private function getMigrator($schema_version = null) { return new Migrator( - __DIR__ . '/test-migrations', + TEST_FIXTURES_PATH . 'migrations', $schema_version ?: $this->getSchemaVersion() ); } - public function testSchemaVersion() - { - $schema_version = $this->getSchemaVersion(); - $this->assertSame(0, $schema_version->get()); - - $schema_version->add(1); - $this->assertTrue($schema_version->contains(1)); - $this->assertSame(1, $schema_version->get()); - - $schema_version->add(2); - $this->assertTrue($schema_version->contains(2)); - $this->assertSame(2, $schema_version->get()); - - $schema_version->remove(1); - $this->assertFalse($schema_version->contains(1)); - $this->assertSame(2, $schema_version->get()); - } - public function testRelevance() { $migrator = $this->getMigrator(); @@ -102,7 +78,7 @@ class MigrationTest extends \Codeception\Test\Unit $relevant = $migrator->relevantMigrations(null); $this->assertSame(4, count($relevant)); - $migrator->migrateTo(10); + $migrator->migrateTo(2); $relevant = $migrator->relevantMigrations(null); $this->assertSame(1, count($relevant)); @@ -113,7 +89,7 @@ class MigrationTest extends \Codeception\Test\Unit $schema_version = $this->getSchemaVersion(); $migrator = $this->getMigrator($schema_version); $migrator->migrateTo(null); - $this->assertSame(20190417, $schema_version->get()); + $this->assertSame(10, $schema_version->get()); $this->assertSame(0, count($migrator->relevantMigrations(null))); return $schema_version; @@ -133,13 +109,12 @@ class MigrationTest extends \Codeception\Test\Unit public function testGaps() { $schema_version = $this->getSchemaVersion(); - $schema_version->add(2); - $schema_version->add(10); + $schema_version->set(10); $migrator = $this->getMigrator($schema_version); $relevant = $migrator->relevantMigrations(null); - $this->assertSame(2, count($relevant)); - $this->assertEquals([1, 20190417], array_keys($relevant)); + $this->assertSame(1, count($relevant)); + $this->assertEquals(['2.1'], array_keys($relevant)); } } diff --git a/tests/unit/lib/classes/test-migrations/20190417_returns_instance.php b/tests/unit/lib/classes/test-migrations/20190417_returns_instance.php deleted file mode 100644 index c55e214167994d386e47fbb530e5b05ec836e9f7..0000000000000000000000000000000000000000 --- a/tests/unit/lib/classes/test-migrations/20190417_returns_instance.php +++ /dev/null @@ -1,5 +0,0 @@ -<?php -return new class() extends Migration -{ - -};