diff --git a/3rdparty b/3rdparty index 32f3284a5ca0e..caeacf211cf4c 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit 32f3284a5ca0e372baaf458dc22aed89e257178a +Subproject commit caeacf211cf4c264d770f2e10492528c6be9d9dd diff --git a/core/Command/Db/Migrations/ExecuteCommand.php b/core/Command/Db/Migrations/ExecuteCommand.php new file mode 100644 index 0000000000000..970aa9b91af59 --- /dev/null +++ b/core/Command/Db/Migrations/ExecuteCommand.php @@ -0,0 +1,61 @@ + + * + * @copyright Copyright (c) 2016, ownCloud GmbH. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OC\Core\Command\Db\Migrations; + +use Doctrine\DBAL\Migrations\Tools\Console\Command\ExecuteCommand as DBALExecuteCommand; +use OC\DB\MigrationService; +use OCP\IConfig; +use OCP\IDBConnection; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class ExecuteCommand extends DBALExecuteCommand { + /** @var IDBConnection */ + private $dbConnection; + + /** + * @param IDBConnection $connection + */ + public function __construct(IDBConnection $connection) { + $this->dbConnection = $connection; + + parent::__construct(); + } + + protected function configure() { + $this->addArgument('app', InputArgument::REQUIRED, 'Name of the app this migration command shall work on'); + + parent::configure(); + } + + public function execute(InputInterface $input, + OutputInterface $output) { + $appName = $input->getArgument('app'); + $ms = new MigrationService(); + $mc = $ms->buildConfiguration($appName, $this->dbConnection); + $this->setMigrationConfiguration($mc); + + parent::execute($input, $output); + } + +} diff --git a/core/Command/Db/Migrations/GenerateCommand.php b/core/Command/Db/Migrations/GenerateCommand.php new file mode 100644 index 0000000000000..ba56a6ee25ab4 --- /dev/null +++ b/core/Command/Db/Migrations/GenerateCommand.php @@ -0,0 +1,59 @@ + + * + * @copyright Copyright (c) 2016, ownCloud GmbH. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OC\Core\Command\Db\Migrations; + +use Doctrine\DBAL\Migrations\Tools\Console\Command\GenerateCommand as DBALGenerateCommand; +use OC\DB\MigrationService; +use OCP\IDBConnection; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class GenerateCommand extends DBALGenerateCommand { + /** @var IDBConnection */ + private $dbConnection; + + /** + * @param IDBConnection $connection + */ + public function __construct(IDBConnection $connection) { + $this->dbConnection = $connection; + + parent::__construct(); + } + + protected function configure() { + $this->addArgument('app', InputArgument::REQUIRED, 'Name of the app this migration command shall work on'); + + parent::configure(); + } + + public function execute(InputInterface $input, OutputInterface $output) { + $appName = $input->getArgument('app'); + $ms = new MigrationService(); + $mc = $ms->buildConfiguration($appName, $this->dbConnection); + $this->setMigrationConfiguration($mc); + + parent::execute($input, $output); + } + +} diff --git a/core/Command/Db/Migrations/MigrateCommand.php b/core/Command/Db/Migrations/MigrateCommand.php new file mode 100644 index 0000000000000..73966515f7476 --- /dev/null +++ b/core/Command/Db/Migrations/MigrateCommand.php @@ -0,0 +1,59 @@ + + * + * @copyright Copyright (c) 2016, ownCloud GmbH. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OC\Core\Command\Db\Migrations; + +use Doctrine\DBAL\Migrations\Tools\Console\Command\MigrateCommand as DBALMigrateCommand; +use OC\DB\MigrationService; +use OCP\IDBConnection; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class MigrateCommand extends DBALMigrateCommand { + /** @var IDBConnection */ + private $dbConnection; + + /** + * @param IDBConnection $connection + */ + public function __construct(IDBConnection $connection) { + $this->dbConnection = $connection; + + parent::__construct(); + } + + protected function configure() { + $this->addArgument('app', InputArgument::REQUIRED, 'Name of the app this migration command shall work on'); + + parent::configure(); + } + + public function execute(InputInterface $input, OutputInterface $output) { + $appName = $input->getArgument('app'); + $ms = new MigrationService(); + $mc = $ms->buildConfiguration($appName, $this->dbConnection); + $this->setMigrationConfiguration($mc); + + parent::execute($input, $output); + } + +} diff --git a/core/Command/Db/Migrations/StatusCommand.php b/core/Command/Db/Migrations/StatusCommand.php new file mode 100644 index 0000000000000..fceae2af7bc59 --- /dev/null +++ b/core/Command/Db/Migrations/StatusCommand.php @@ -0,0 +1,60 @@ + + * + * @copyright Copyright (c) 2016, ownCloud GmbH. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OC\Core\Command\Db\Migrations; + +use Doctrine\DBAL\Migrations\Tools\Console\Command\StatusCommand as DBALStatusCommand; +use OC\DB\MigrationService; +use OCP\IDBConnection; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class StatusCommand extends DBALStatusCommand { + + /** @var IDBConnection */ + private $dbConnection; + + /** + * @param IDBConnection $connection + */ + public function __construct(IDBConnection $connection) { + $this->dbConnection = $connection; + + parent::__construct(); + } + + protected function configure() { + parent::configure(); + + $this->addArgument('app', InputArgument::REQUIRED, 'Name of the app this migration command shall work on'); + } + + public function execute(InputInterface $input, OutputInterface $output) { + $appName = $input->getArgument('app'); + $ms = new MigrationService(); + $mc = $ms->buildConfiguration($appName, $this->dbConnection); + $this->setMigrationConfiguration($mc); + + parent::execute($input, $output); + } + +} diff --git a/core/register_command.php b/core/register_command.php index 6f31adafe926f..a99b7d7327045 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -84,6 +84,10 @@ $application->add(new OC\Core\Command\Db\GenerateChangeScript()); $application->add(new OC\Core\Command\Db\ConvertType(\OC::$server->getConfig(), new \OC\DB\ConnectionFactory(\OC::$server->getConfig()))); + $application->add(new OC\Core\Command\Db\Migrations\StatusCommand(\OC::$server->getDatabaseConnection())); + $application->add(new OC\Core\Command\Db\Migrations\MigrateCommand(\OC::$server->getDatabaseConnection())); + $application->add(new OC\Core\Command\Db\Migrations\GenerateCommand(\OC::$server->getDatabaseConnection())); + $application->add(new OC\Core\Command\Db\Migrations\ExecuteCommand(\OC::$server->getDatabaseConnection())); $application->add(new OC\Core\Command\Encryption\Disable(\OC::$server->getConfig())); $application->add(new OC\Core\Command\Encryption\Enable(\OC::$server->getConfig(), \OC::$server->getEncryptionManager())); diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 7a4cc6eaa8461..529934b7c6dec 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -374,6 +374,10 @@ 'OC\\Core\\Command\\Config\\System\\SetConfig' => $baseDir . '/core/Command/Config/System/SetConfig.php', 'OC\\Core\\Command\\Db\\ConvertType' => $baseDir . '/core/Command/Db/ConvertType.php', 'OC\\Core\\Command\\Db\\GenerateChangeScript' => $baseDir . '/core/Command/Db/GenerateChangeScript.php', + 'OC\\Core\\Command\\Db\\Migrations\\ExecuteCommand' => $baseDir . '/core/Command/Db/Migrations/ExecuteCommand.php', + 'OC\\Core\\Command\\Db\\Migrations\\GenerateCommand' => $baseDir . '/core/Command/Db/Migrations/GenerateCommand.php', + 'OC\\Core\\Command\\Db\\Migrations\\MigrateCommand' => $baseDir . '/core/Command/Db/Migrations/MigrateCommand.php', + 'OC\\Core\\Command\\Db\\Migrations\\StatusCommand' => $baseDir . '/core/Command/Db/Migrations/StatusCommand.php', 'OC\\Core\\Command\\Encryption\\ChangeKeyStorageRoot' => $baseDir . '/core/Command/Encryption/ChangeKeyStorageRoot.php', 'OC\\Core\\Command\\Encryption\\DecryptAll' => $baseDir . '/core/Command/Encryption/DecryptAll.php', 'OC\\Core\\Command\\Encryption\\Disable' => $baseDir . '/core/Command/Encryption/Disable.php', @@ -439,7 +443,9 @@ 'OC\\DB\\MDB2SchemaManager' => $baseDir . '/lib/private/DB/MDB2SchemaManager.php', 'OC\\DB\\MDB2SchemaReader' => $baseDir . '/lib/private/DB/MDB2SchemaReader.php', 'OC\\DB\\MDB2SchemaWriter' => $baseDir . '/lib/private/DB/MDB2SchemaWriter.php', + 'OC\\DB\\MigrationConfiguration' => $baseDir . '/lib/private/DB/MigrationConfiguration.php', 'OC\\DB\\MigrationException' => $baseDir . '/lib/private/DB/MigrationException.php', + 'OC\\DB\\MigrationService' => $baseDir . '/lib/private/DB/MigrationService.php', 'OC\\DB\\Migrator' => $baseDir . '/lib/private/DB/Migrator.php', 'OC\\DB\\MySQLMigrator' => $baseDir . '/lib/private/DB/MySQLMigrator.php', 'OC\\DB\\NoCheckMigrator' => $baseDir . '/lib/private/DB/NoCheckMigrator.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 6a063f0d8c790..66d9aa1ecf38c 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -404,6 +404,10 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Core\\Command\\Config\\System\\SetConfig' => __DIR__ . '/../../..' . '/core/Command/Config/System/SetConfig.php', 'OC\\Core\\Command\\Db\\ConvertType' => __DIR__ . '/../../..' . '/core/Command/Db/ConvertType.php', 'OC\\Core\\Command\\Db\\GenerateChangeScript' => __DIR__ . '/../../..' . '/core/Command/Db/GenerateChangeScript.php', + 'OC\\Core\\Command\\Db\\Migrations\\ExecuteCommand' => __DIR__ . '/../../..' . '/core/Command/Db/Migrations/ExecuteCommand.php', + 'OC\\Core\\Command\\Db\\Migrations\\GenerateCommand' => __DIR__ . '/../../..' . '/core/Command/Db/Migrations/GenerateCommand.php', + 'OC\\Core\\Command\\Db\\Migrations\\MigrateCommand' => __DIR__ . '/../../..' . '/core/Command/Db/Migrations/MigrateCommand.php', + 'OC\\Core\\Command\\Db\\Migrations\\StatusCommand' => __DIR__ . '/../../..' . '/core/Command/Db/Migrations/StatusCommand.php', 'OC\\Core\\Command\\Encryption\\ChangeKeyStorageRoot' => __DIR__ . '/../../..' . '/core/Command/Encryption/ChangeKeyStorageRoot.php', 'OC\\Core\\Command\\Encryption\\DecryptAll' => __DIR__ . '/../../..' . '/core/Command/Encryption/DecryptAll.php', 'OC\\Core\\Command\\Encryption\\Disable' => __DIR__ . '/../../..' . '/core/Command/Encryption/Disable.php', @@ -469,7 +473,9 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\DB\\MDB2SchemaManager' => __DIR__ . '/../../..' . '/lib/private/DB/MDB2SchemaManager.php', 'OC\\DB\\MDB2SchemaReader' => __DIR__ . '/../../..' . '/lib/private/DB/MDB2SchemaReader.php', 'OC\\DB\\MDB2SchemaWriter' => __DIR__ . '/../../..' . '/lib/private/DB/MDB2SchemaWriter.php', + 'OC\\DB\\MigrationConfiguration' => __DIR__ . '/../../..' . '/lib/private/DB/MigrationConfiguration.php', 'OC\\DB\\MigrationException' => __DIR__ . '/../../..' . '/lib/private/DB/MigrationException.php', + 'OC\\DB\\MigrationService' => __DIR__ . '/../../..' . '/lib/private/DB/MigrationService.php', 'OC\\DB\\Migrator' => __DIR__ . '/../../..' . '/lib/private/DB/Migrator.php', 'OC\\DB\\MySQLMigrator' => __DIR__ . '/../../..' . '/lib/private/DB/MySQLMigrator.php', 'OC\\DB\\NoCheckMigrator' => __DIR__ . '/../../..' . '/lib/private/DB/NoCheckMigrator.php', diff --git a/lib/private/DB/MDB2SchemaReader.php b/lib/private/DB/MDB2SchemaReader.php index c198bb31e0011..205fe79f2b7d9 100644 --- a/lib/private/DB/MDB2SchemaReader.php +++ b/lib/private/DB/MDB2SchemaReader.php @@ -80,8 +80,10 @@ public function __construct(IConfig $config, AbstractPlatform $platform) { * @return \Doctrine\DBAL\Schema\Schema * @throws \DomainException */ - public function loadSchemaFromFile($file) { - $schema = new \Doctrine\DBAL\Schema\Schema(); + public function loadSchemaFromFile($file, $schema = null) { + if (is_null($schema)) { + $schema = new \Doctrine\DBAL\Schema\Schema(); + } $loadEntities = libxml_disable_entity_loader(false); $xml = simplexml_load_file($file); libxml_disable_entity_loader($loadEntities); diff --git a/lib/private/DB/MigrationConfiguration.php b/lib/private/DB/MigrationConfiguration.php new file mode 100644 index 0000000000000..cc8b592064f76 --- /dev/null +++ b/lib/private/DB/MigrationConfiguration.php @@ -0,0 +1,52 @@ + + * + * @copyright Copyright (c) 2016, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ +namespace OC\DB; + +use Doctrine\DBAL\Migrations\Finder\MigrationFinderInterface; +use Doctrine\DBAL\Migrations\OutputWriter; +use Doctrine\DBAL\Platforms\OraclePlatform; + +class MigrationConfiguration extends \Doctrine\DBAL\Migrations\Configuration\Configuration { + + function __construct(Connection $connection, OutputWriter $outputWriter = null, MigrationFinderInterface $finder = null) { + parent::__construct($connection, $outputWriter, $finder); + + $this->setMigrationsTableName($this->getMigrationsTableName()); + $this->setMigrationsColumnName($this->getMigrationsColumnName()); + } + + public function setMigrationsColumnName($columnName) { + $platform = $this->getConnection()->getDatabasePlatform(); + if ($platform instanceof OraclePlatform) { + $columnName = $platform->quoteIdentifier($columnName); + } + parent::setMigrationsColumnName($columnName); + } + + public function setMigrationsTableName($tableName) { + $platform = $this->getConnection()->getDatabasePlatform(); + if ($platform instanceof OraclePlatform) { + $tableName = $platform->quoteIdentifier($tableName); + } + parent::setMigrationsTableName($tableName); + } + +} diff --git a/lib/private/DB/MigrationService.php b/lib/private/DB/MigrationService.php new file mode 100644 index 0000000000000..87a64f192e33f --- /dev/null +++ b/lib/private/DB/MigrationService.php @@ -0,0 +1,76 @@ + + * + * @copyright Copyright (c) 2016, ownCloud GmbH. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OC\DB; + +use Doctrine\DBAL\Migrations\Configuration\Configuration; +use Doctrine\DBAL\Migrations\Migration; +use Doctrine\DBAL\Migrations\OutputWriter; +use OCP\IDBConnection; + +class MigrationService { + + /** + * @param string $appName + * @param IDBConnection $connection + * @return MigrationConfiguration + * @throws \Exception + */ + public function buildConfiguration($appName, IDBConnection $connection) { + if ($appName === 'core') { + $migrationsPath = \OC::$SERVERROOT . '/core/Migrations'; + $migrationsNamespace = 'OC\\Migrations'; + } else { + $appPath = \OC_App::getAppPath($appName); + if (!$appPath) { + throw new \InvalidArgumentException('Path to app is not defined.'); + } + $migrationsPath = "$appPath/appinfo/Migrations"; + $migrationsNamespace = "OCA\\$appName\\Migrations"; + } + + if (!is_dir($migrationsPath)) { + if (!mkdir($migrationsPath)) { + throw new \Exception("Could not create migration folder \"$migrationsPath\""); + }; + } + $prefix = $connection->getPrefix(); + $mc = new MigrationConfiguration($connection); + $mc->setMigrationsDirectory($migrationsPath); + $mc->setMigrationsNamespace($migrationsNamespace); + $mc->setMigrationsTableName("{$prefix}{$appName}_migration_versions"); + return $mc; + } + + /** + * @param Configuration $migrationConfiguration + * @param bool $noMigrationException + */ + public function migrate($migrationConfiguration, $noMigrationException = false) { + $migrationConfiguration->setOutputWriter(new OutputWriter(function ($message){ + \OCP\Util::writeLog('migrations', $message, \OCP\Util::INFO); + })); + + $migration = new Migration($migrationConfiguration); + $migration->setNoMigrationException($noMigrationException); + $migration->migrate(); + } +} diff --git a/lib/private/DB/OracleConnection.php b/lib/private/DB/OracleConnection.php index 08d713651722a..106f219d1a9ba 100644 --- a/lib/private/DB/OracleConnection.php +++ b/lib/private/DB/OracleConnection.php @@ -30,9 +30,14 @@ class OracleConnection extends Connection { * Quote the keys of the array */ private function quoteKeys(array $data) { - $return = array(); + $return = []; + $c = $this->getDatabasePlatform()->getIdentifierQuoteCharacter(); foreach($data as $key => $value) { - $return[$this->quoteIdentifier($key)] = $value; + if ($key[0] !== $c) { + $return[$this->quoteIdentifier($key)] = $value; + } else { + $return[$key] = $value; + } } return $return; } @@ -40,8 +45,10 @@ private function quoteKeys(array $data) { /** * {@inheritDoc} */ - public function insert($tableName, array $data, array $types = array()) { - $tableName = $this->quoteIdentifier($tableName); + public function insert($tableName, array $data, array $types = []) { + if ($tableName[0] !== $this->getDatabasePlatform()->getIdentifierQuoteCharacter()) { + $tableName = $this->quoteIdentifier($tableName); + } $data = $this->quoteKeys($data); return parent::insert($tableName, $data, $types); } @@ -49,8 +56,10 @@ public function insert($tableName, array $data, array $types = array()) { /** * {@inheritDoc} */ - public function update($tableName, array $data, array $identifier, array $types = array()) { - $tableName = $this->quoteIdentifier($tableName); + public function update($tableName, array $data, array $identifier, array $types = []) { + if ($tableName[0] !== $this->getDatabasePlatform()->getIdentifierQuoteCharacter()) { + $tableName = $this->quoteIdentifier($tableName); + } $data = $this->quoteKeys($data); $identifier = $this->quoteKeys($identifier); return parent::update($tableName, $data, $identifier, $types); @@ -59,10 +68,12 @@ public function update($tableName, array $data, array $identifier, array $types /** * {@inheritDoc} */ - public function delete($tableExpression, array $identifier, array $types = array()) { - $tableName = $this->quoteIdentifier($tableExpression); + public function delete($tableExpression, array $identifier, array $types = []) { + if ($tableExpression[0] !== $this->getDatabasePlatform()->getIdentifierQuoteCharacter()) { + $tableExpression = $this->quoteIdentifier($tableExpression); + } $identifier = $this->quoteKeys($identifier); - return parent::delete($tableName, $identifier); + return parent::delete($tableExpression, $identifier); } /** diff --git a/lib/private/Installer.php b/lib/private/Installer.php index 38d0ce1368473..3073b8ce64126 100644 --- a/lib/private/Installer.php +++ b/lib/private/Installer.php @@ -102,11 +102,17 @@ public function installApp($appId) { $info = OC_App::getAppInfo($basedir.'/appinfo/info.xml', true); //install the database - if(is_file($basedir.'/appinfo/database.xml')) { - if (\OC::$server->getAppConfig()->getValue($info['id'], 'installed_version') === null) { - OC_DB::createDbFromStructure($basedir.'/appinfo/database.xml'); - } else { - OC_DB::updateDbFromStructure($basedir.'/appinfo/database.xml'); + if (isset($appData['use-migrations']) && $appData['use-migrations'] === 'true') { + $ms = new \OC\DB\MigrationService(); + $mc = $ms->buildConfiguration($appId, \OC::$server->getDatabaseConnection()); + $ms->migrate($mc); + } else { + if(is_file($basedir.'/appinfo/database.xml')) { + if (\OC::$server->getAppConfig()->getValue($info['id'], 'installed_version') === null) { + OC_DB::createDbFromStructure($basedir . '/appinfo/database.xml'); + } else { + OC_DB::updateDbFromStructure($basedir . '/appinfo/database.xml'); + } } } @@ -472,17 +478,23 @@ public static function installShippedApps($softErrors = false) { * @return integer */ public static function installShippedApp($app) { + + $info = OC_App::getAppInfo($app); + if (is_null($info)) { + return false; + } + //install the database $appPath = OC_App::getAppPath($app); - if(is_file("$appPath/appinfo/database.xml")) { - try { - OC_DB::createDbFromStructure("$appPath/appinfo/database.xml"); - } catch (TableExistsException $e) { - throw new HintException( - 'Failed to enable app ' . $app, - 'Please ask for help via one of our support channels.', - 0, $e - ); + if (isset($info['use-migrations']) && $info['use-migrations'] === 'true') { + $ms = new \OC\DB\MigrationService(); + $mc = $ms->buildConfiguration($app, \OC::$server->getDatabaseConnection()); + $ms->migrate($mc); + } else { + if (\OC::$server->getAppConfig()->getValue($info['id'], 'installed_version') === null) { + if (is_file($appPath . '/appinfo/database.xml')) { + OC_DB::createDbFromStructure($appPath . '/appinfo/database.xml'); + } } } @@ -490,10 +502,6 @@ public static function installShippedApp($app) { \OC_App::registerAutoloading($app, $appPath); self::includeAppScript("$appPath/appinfo/install.php"); - $info = OC_App::getAppInfo($app); - if (is_null($info)) { - return false; - } \OC_App::setupBackgroundJobs($info['background-jobs']); OC_App::executeRepairSteps($app, $info['repair-steps']['install']); diff --git a/lib/private/Setup.php b/lib/private/Setup.php index 81a5343fe2103..944f3a01a9eeb 100644 --- a/lib/private/Setup.php +++ b/lib/private/Setup.php @@ -330,6 +330,8 @@ public function install($options) { try { $dbSetup->initialize($options); $dbSetup->setupDatabase($username); + // apply necessary migrations + $dbSetup->runMigrations(); } catch (\OC\DatabaseSetupException $e) { $error[] = array( 'error' => $e->getMessage(), diff --git a/lib/private/Setup/AbstractDatabase.php b/lib/private/Setup/AbstractDatabase.php index dbf46888ffe3c..cae46efd33932 100644 --- a/lib/private/Setup/AbstractDatabase.php +++ b/lib/private/Setup/AbstractDatabase.php @@ -28,6 +28,7 @@ use OC\AllConfig; use OC\DB\ConnectionFactory; +use OC\DB\MigrationService; use OCP\IConfig; use OCP\IL10N; use OCP\ILogger; @@ -144,4 +145,13 @@ protected function connect(array $configOverwrite = []) { * @param string $userName */ abstract public function setupDatabase($userName); + + public function runMigrations() { + if (!is_dir(\OC::$SERVERROOT."/core/Migrations")) { + return; + } + $ms = new MigrationService(); + $mc = $ms->buildConfiguration('core', \OC::$server->getDatabaseConnection()); + $ms->migrate($mc, true); + } } diff --git a/lib/private/Updater.php b/lib/private/Updater.php index a66d49941cd88..46471f0acfca6 100644 --- a/lib/private/Updater.php +++ b/lib/private/Updater.php @@ -284,8 +284,13 @@ private function doUpgrade($currentVersion, $installedVersion) { protected function doCoreUpgrade() { $this->emit('\OC\Updater', 'dbUpgradeBefore'); - // do the real upgrade - \OC_DB::updateDbFromStructure(\OC::$SERVERROOT . '/db_structure.xml'); + // execute core migrations + if (is_dir(\OC::$SERVERROOT."/core/Migrations")) { + $ms = new \OC\DB\MigrationService(); + $mc = $ms->buildConfiguration('core', \OC::$server->getDatabaseConnection()); + + $ms->migrate($mc, true); + } $this->emit('\OC\Updater', 'dbUpgrade'); } diff --git a/lib/private/legacy/app.php b/lib/private/legacy/app.php index adf29601ac6d1..169fe96bcf54c 100644 --- a/lib/private/legacy/app.php +++ b/lib/private/legacy/app.php @@ -1122,8 +1122,14 @@ public static function updateApp($appId) { } $appData = self::getAppInfo($appId); self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']); - if (file_exists($appPath . '/appinfo/database.xml')) { - OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml'); + if (isset($appData['use-migrations']) && $appData['use-migrations'] === 'true') { + $ms = new \OC\DB\MigrationService(); + $mc = $ms->buildConfiguration($appId, \OC::$server->getDatabaseConnection()); + $ms->migrate($mc, true); + } else { + if (file_exists($appPath . '/appinfo/database.xml')) { + OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml'); + } } self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']); self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']); diff --git a/tests/lib/DB/MigrationsTest.php b/tests/lib/DB/MigrationsTest.php new file mode 100644 index 0000000000000..0ac82af0a801b --- /dev/null +++ b/tests/lib/DB/MigrationsTest.php @@ -0,0 +1,29 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + + +namespace Test\DB; + +use OC\DB\MigrationService; + +/** + * Class MigrationsTest + * + * @group DB + * + * @package Test\DB + */ +class MigrationsTest extends \Test\TestCase { + public function testMigrationTableCreation() { + $m = new MigrationService(); + $appName = 'testing'; + $conf = $m->buildConfiguration($appName, \OC::$server->getDatabaseConnection()); + $this->assertTrue($conf->createMigrationTable()); + $this->assertFalse($conf->createMigrationTable()); + } +}