From 5b97369b00afbdf55eed145be9ac981dca06d2a9 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 4 Jun 2014 16:40:53 +0200 Subject: [PATCH 1/3] Simulate apps database schema update on upgrade When upgrade, also simulate the database schema update for apps before doing the actual upgrade. --- core/ajax/update.php | 3 ++ core/command/upgrade.php | 3 ++ core/js/update.js | 2 +- lib/private/app.php | 51 ++++++++++++++--------- lib/private/db.php | 12 ++++-- lib/private/db/mdb2schemamanager.php | 11 +++-- lib/private/updater.php | 60 ++++++++++++++++++++++------ 7 files changed, 103 insertions(+), 39 deletions(-) diff --git a/core/ajax/update.php b/core/ajax/update.php index 84d7a21209..698614c975 100644 --- a/core/ajax/update.php +++ b/core/ajax/update.php @@ -15,6 +15,9 @@ if (OC::checkUpgrade(false)) { $updater->listen('\OC\Updater', 'dbUpgrade', function () use ($eventSource, $l) { $eventSource->send('success', (string)$l->t('Updated database')); }); + $updater->listen('\OC\Updater', 'dbSimulateUpgrade', function () use ($eventSource, $l) { + $eventSource->send('success', (string)$l->t('Checked database schema update')); + }); $updater->listen('\OC\Updater', 'disabledApps', function ($appList) use ($eventSource, $l) { $list = array(); foreach ($appList as $appId) { diff --git a/core/command/upgrade.php b/core/command/upgrade.php index 8ce8ef9b6e..c3946d2aab 100644 --- a/core/command/upgrade.php +++ b/core/command/upgrade.php @@ -56,6 +56,9 @@ class Upgrade extends Command { $updater->listen('\OC\Updater', 'dbUpgrade', function () use($output) { $output->writeln('Updated database'); }); + $updater->listen('\OC\Updater', 'dbSimulateUpgrade', function () use($output) { + $output->writeln('Checked database schema update'); + }); $updater->listen('\OC\Updater', 'disabledApps', function ($appList) use($output) { $output->writeln('Disabled incompatible apps: ' . implode(', ', $appList) . ''); }); diff --git a/core/js/update.js b/core/js/update.js index cc0f541bd7..e5ce322df9 100644 --- a/core/js/update.js +++ b/core/js/update.js @@ -28,7 +28,7 @@ this.addMessage(t( 'core', 'Updating {productName} to version {version}, this may take a while.', { - productName: OC.theme.name, + productName: OC.theme.name || 'ownCloud', version: OC.config.versionstring }), 'bold' diff --git a/lib/private/app.php b/lib/private/app.php index 52f77535a5..2650ad98bc 100644 --- a/lib/private/app.php +++ b/lib/private/app.php @@ -875,6 +875,18 @@ class OC_App { } } + public static function shouldUpgrade($app) { + $versions = self::getAppVersions(); + $currentVersion = OC_App::getAppVersion($app); + if ($currentVersion) { + $installedVersion = $versions[$app]; + if (version_compare($currentVersion, $installedVersion, '>')) { + return true; + } + } + return false; + } + /** * check if the app needs updating and update when needed * @@ -885,26 +897,27 @@ class OC_App { return; } self::$checkedApps[] = $app; - $versions = self::getAppVersions(); - $currentVersion = OC_App::getAppVersion($app); - if ($currentVersion) { - $installedVersion = $versions[$app]; - if (version_compare($currentVersion, $installedVersion, '>')) { - $info = self::getAppInfo($app); - OC_Log::write($app, - 'starting app upgrade from ' . $installedVersion . ' to ' . $currentVersion, - OC_Log::DEBUG); - try { - OC_App::updateApp($app); - OC_Hook::emit('update', 'success', 'Updated ' . $info['name'] . ' app'); - } catch (Exception $e) { - OC_Hook::emit('update', 'failure', 'Failed to update ' . $info['name'] . ' app: ' . $e->getMessage()); - $l = OC_L10N::get('lib'); - throw new RuntimeException($l->t('Failed to upgrade "%s".', array($app)), 0, $e); - } - OC_Appconfig::setValue($app, 'installed_version', OC_App::getAppVersion($app)); - } + if (!self::shouldUpgrade($app)) { + return; } + $versions = self::getAppVersions(); + $installedVersion = $versions[$app]; + $currentVersion = OC_App::getAppVersion($app); + OC_Log::write( + $app, + 'starting app upgrade from ' . $installedVersion . ' to ' . $currentVersion, + OC_Log::DEBUG + ); + $info = self::getAppInfo($app); + try { + OC_App::updateApp($app); + OC_Hook::emit('update', 'success', 'Updated ' . $info['name'] . ' app'); + } catch (Exception $e) { + OC_Hook::emit('update', 'failure', 'Failed to update ' . $info['name'] . ' app: ' . $e->getMessage()); + $l = OC_L10N::get('lib'); + throw new RuntimeException($l->t('Failed to upgrade "%s".', array($app)), 0, $e); + } + OC_Appconfig::setValue($app, 'installed_version', OC_App::getAppVersion($app)); } /** diff --git a/lib/private/db.php b/lib/private/db.php index 422f783c74..f6854e3e16 100644 --- a/lib/private/db.php +++ b/lib/private/db.php @@ -307,15 +307,21 @@ class OC_DB { /** * update the database schema * @param string $file file to read structure from + * @param bool $simulate whether to simulate the upgrade on separate tables * @throws Exception * @return string|boolean */ - public static function updateDbFromStructure($file) { + public static function updateDbFromStructure($file, $simulate = false) { $schemaManager = self::getMDB2SchemaManager(); try { - $result = $schemaManager->updateDbFromStructure($file); + $result = $schemaManager->updateDbFromStructure($file, false, $simulate); } catch (Exception $e) { - OC_Log::write('core', 'Failed to update database structure ('.$e.')', OC_Log::FATAL); + if ($simulate) { + OC_Log::write('core', 'Database structure update simulation failed ('.$e.')', OC_Log::FATAL); + } + else { + OC_Log::write('core', 'Failed to update database structure ('.$e.')', OC_Log::FATAL); + } throw $e; } return $result; diff --git a/lib/private/db/mdb2schemamanager.php b/lib/private/db/mdb2schemamanager.php index 533ed9110e..397aaf3608 100644 --- a/lib/private/db/mdb2schemamanager.php +++ b/lib/private/db/mdb2schemamanager.php @@ -77,9 +77,10 @@ class MDB2SchemaManager { * update the database scheme * @param string $file file to read structure from * @param bool $generateSql only return the sql needed for the upgrade + * @param bool $simulate whether to simulate on separate tables instead of the real onces * @return string|boolean */ - public function updateDbFromStructure($file, $generateSql = false) { + public function updateDbFromStructure($file, $generateSql = false, $simulate = false) { $platform = $this->conn->getDatabasePlatform(); $schemaReader = new MDB2SchemaReader(\OC_Config::getObject(), $platform); @@ -89,8 +90,12 @@ class MDB2SchemaManager { if ($generateSql) { return $migrator->generateChangeScript($toSchema); } else { - $migrator->checkMigrate($toSchema); - $migrator->migrate($toSchema); + if ($simulate) { + $migrator->checkMigrate($toSchema); + } + else { + $migrator->migrate($toSchema); + } return true; } } diff --git a/lib/private/updater.php b/lib/private/updater.php index 58d3cab73a..1c363123e1 100644 --- a/lib/private/updater.php +++ b/lib/private/updater.php @@ -125,29 +125,63 @@ class Updater extends BasicEmitter { * STOP CONFIG CHANGES FOR OLDER VERSIONS */ + $canUpgrade = false; + // simulate DB upgrade try { - \OC_DB::updateDbFromStructure(\OC::$SERVERROOT . '/db_structure.xml'); - $this->emit('\OC\Updater', 'dbUpgrade'); + // simulate core DB upgrade + \OC_DB::updateDbFromStructure(\OC::$SERVERROOT . '/db_structure.xml', true); + // simulate apps DB upgrade + $version = \OC_Util::getVersion(); + $apps = \OC_App::getEnabledApps(); + foreach ($apps as $appId) { + $info = \OC_App::getAppInfo($appId); + if (\OC_App::isAppCompatible($version, $info) && \OC_App::shouldUpgrade($appId)) { + if (file_exists(\OC_App::getAppPath($appId) . '/appinfo/database.xml')) { + \OC_DB::updateDbFromStructure(\OC_App::getAppPath($appId) . '/appinfo/database.xml', true); + } + } + } + + $this->emit('\OC\Updater', 'dbSimulateUpgrade'); + + $canUpgrade = true; } catch (\Exception $exception) { $this->emit('\OC\Updater', 'failure', array($exception->getMessage())); } - \OC_Config::setValue('version', implode('.', \OC_Util::getVersion())); - $disabledApps = \OC_App::checkAppsRequirements(); - if (!empty($disabledApps)) { - $this->emit('\OC\Updater', 'disabledApps', array($disabledApps)); + + if ($canUpgrade) { + // proceed with real upgrade + try { + // do the real upgrade + \OC_DB::updateDbFromStructure(\OC::$SERVERROOT . '/db_structure.xml'); + $this->emit('\OC\Updater', 'dbUpgrade'); + + } catch (\Exception $exception) { + $this->emit('\OC\Updater', 'failure', array($exception->getMessage())); + return false; + } + // TODO: why not do this at the end ? + \OC_Config::setValue('version', implode('.', \OC_Util::getVersion())); + $disabledApps = \OC_App::checkAppsRequirements(); + if (!empty($disabledApps)) { + $this->emit('\OC\Updater', 'disabledApps', array($disabledApps)); + } + // load all apps to also upgrade enabled apps + \OC_App::loadApps(); + + $repair = new Repair(); + $repair->run(); + + //Invalidate update feed + \OC_Appconfig::setValue('core', 'lastupdatedat', 0); } - // load all apps to also upgrade enabled apps - \OC_App::loadApps(); - $repair = new Repair(); - $repair->run(); - - //Invalidate update feed - \OC_Appconfig::setValue('core', 'lastupdatedat', 0); \OC_Config::setValue('maintenance', false); $this->emit('\OC\Updater', 'maintenanceEnd'); + + return $canUpgrade; } } From d4ffafe4674dbda984c78ea9f7db894156e2a764 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 4 Jun 2014 18:15:58 +0200 Subject: [PATCH 2/3] Removed simulate db update flag and split into separate methods --- lib/private/db.php | 29 +++++++++++++++------- lib/private/db/mdb2schemamanager.php | 37 +++++++++++++++++++--------- lib/private/updater.php | 4 +-- 3 files changed, 47 insertions(+), 23 deletions(-) diff --git a/lib/private/db.php b/lib/private/db.php index f6854e3e16..82affe293e 100644 --- a/lib/private/db.php +++ b/lib/private/db.php @@ -307,21 +307,32 @@ class OC_DB { /** * update the database schema * @param string $file file to read structure from - * @param bool $simulate whether to simulate the upgrade on separate tables * @throws Exception * @return string|boolean */ - public static function updateDbFromStructure($file, $simulate = false) { + public static function updateDbFromStructure($file) { $schemaManager = self::getMDB2SchemaManager(); try { - $result = $schemaManager->updateDbFromStructure($file, false, $simulate); + $result = $schemaManager->updateDbFromStructure($file); } catch (Exception $e) { - if ($simulate) { - OC_Log::write('core', 'Database structure update simulation failed ('.$e.')', OC_Log::FATAL); - } - else { - OC_Log::write('core', 'Failed to update database structure ('.$e.')', OC_Log::FATAL); - } + OC_Log::write('core', 'Failed to update database structure ('.$e.')', OC_Log::FATAL); + throw $e; + } + return $result; + } + + /** + * simulate the database schema update + * @param string $file file to read structure from + * @throws Exception + * @return string|boolean + */ + public static function simulateUpdateDbFromStructure($file) { + $schemaManager = self::getMDB2SchemaManager(); + try { + $result = $schemaManager->simulateUpdateDbFromStructure($file); + } catch (Exception $e) { + OC_Log::write('core', 'Simulated database structure update failed ('.$e.')', OC_Log::FATAL); throw $e; } return $result; diff --git a/lib/private/db/mdb2schemamanager.php b/lib/private/db/mdb2schemamanager.php index 397aaf3608..734ba18d1a 100644 --- a/lib/private/db/mdb2schemamanager.php +++ b/lib/private/db/mdb2schemamanager.php @@ -73,33 +73,46 @@ class MDB2SchemaManager { } } + /** + * Reads database schema from file + * + * @param string $file file to read from + */ + private function readSchemaFromFile($file) { + $platform = $this->conn->getDatabasePlatform(); + $schemaReader = new MDB2SchemaReader(\OC_Config::getObject(), $platform); + return $schemaReader->loadSchemaFromFile($file); + } + /** * update the database scheme * @param string $file file to read structure from * @param bool $generateSql only return the sql needed for the upgrade - * @param bool $simulate whether to simulate on separate tables instead of the real onces * @return string|boolean */ - public function updateDbFromStructure($file, $generateSql = false, $simulate = false) { - - $platform = $this->conn->getDatabasePlatform(); - $schemaReader = new MDB2SchemaReader(\OC_Config::getObject(), $platform); - $toSchema = $schemaReader->loadSchemaFromFile($file); + public function updateDbFromStructure($file, $generateSql = false) { + $toSchema = $this->readSchemaFromFile($file); $migrator = $this->getMigrator(); if ($generateSql) { return $migrator->generateChangeScript($toSchema); } else { - if ($simulate) { - $migrator->checkMigrate($toSchema); - } - else { - $migrator->migrate($toSchema); - } + $migrator->migrate($toSchema); return true; } } + /** + * update the database scheme + * @param string $file file to read structure from + * @return string|boolean + */ + public function simulateUpdateDbFromStructure($file) { + $toSchema = $this->readSchemaFromFile($file); + $migrator = $this->getMigrator()->checkMigrate($toSchema); + return true; + } + /** * @param \Doctrine\DBAL\Schema\Schema $schema * @return string diff --git a/lib/private/updater.php b/lib/private/updater.php index 1c363123e1..106970c412 100644 --- a/lib/private/updater.php +++ b/lib/private/updater.php @@ -130,7 +130,7 @@ class Updater extends BasicEmitter { // simulate DB upgrade try { // simulate core DB upgrade - \OC_DB::updateDbFromStructure(\OC::$SERVERROOT . '/db_structure.xml', true); + \OC_DB::simulateUpdateDbFromStructure(\OC::$SERVERROOT . '/db_structure.xml'); // simulate apps DB upgrade $version = \OC_Util::getVersion(); @@ -139,7 +139,7 @@ class Updater extends BasicEmitter { $info = \OC_App::getAppInfo($appId); if (\OC_App::isAppCompatible($version, $info) && \OC_App::shouldUpgrade($appId)) { if (file_exists(\OC_App::getAppPath($appId) . '/appinfo/database.xml')) { - \OC_DB::updateDbFromStructure(\OC_App::getAppPath($appId) . '/appinfo/database.xml', true); + \OC_DB::simulateUpdateDbFromStructure(\OC_App::getAppPath($appId) . '/appinfo/database.xml'); } } } From 95fda3c17c6d03bead4b6a754f1e9a71d3cc7e50 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 4 Jun 2014 18:52:52 +0200 Subject: [PATCH 3/3] Do not load apps when upgrade is needed This prevents routes like "core/js/oc.js" to automatically load apps and trigger their update prematurely. --- lib/base.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/base.php b/lib/base.php index 376bb6c1d0..608fe16dce 100644 --- a/lib/base.php +++ b/lib/base.php @@ -730,7 +730,7 @@ class OC { if (!self::$CLI and (!isset($_GET["logout"]) or ($_GET["logout"] !== 'true'))) { try { - if (!OC_Config::getValue('maintenance', false)) { + if (!OC_Config::getValue('maintenance', false) && !self::needUpgrade()) { OC_App::loadApps(); } self::checkSingleUserMode();