diff options
Diffstat (limited to 'lib/private/DB/Migrator.php')
-rw-r--r-- | lib/private/DB/Migrator.php | 206 |
1 files changed, 33 insertions, 173 deletions
diff --git a/lib/private/DB/Migrator.php b/lib/private/DB/Migrator.php index dcf0db89f72..40f8ad9676a 100644 --- a/lib/private/DB/Migrator.php +++ b/lib/private/DB/Migrator.php @@ -1,88 +1,45 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author martin-rueegg <martin.rueegg@metaworx.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author tbelau666 <thomas.belau@gmx.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Victor Dubiniuk <dubiniuk@owncloud.com> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @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 <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OC\DB; +use Doctrine\DBAL\Connection; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Schema\AbstractAsset; -use Doctrine\DBAL\Schema\Comparator; -use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\Schema; -use Doctrine\DBAL\Schema\SchemaConfig; -use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Schema\SchemaDiff; use Doctrine\DBAL\Types\StringType; use Doctrine\DBAL\Types\Type; +use OCP\EventDispatcher\IEventDispatcher; use OCP\IConfig; -use OCP\Security\ISecureRandom; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\GenericEvent; use function preg_match; class Migrator { - - /** @var \Doctrine\DBAL\Connection */ + /** @var Connection */ protected $connection; - /** @var ISecureRandom */ - private $random; - /** @var IConfig */ protected $config; - /** @var EventDispatcherInterface */ - private $dispatcher; + private ?IEventDispatcher $dispatcher; /** @var bool */ private $noEmit = false; - /** - * @param \Doctrine\DBAL\Connection $connection - * @param ISecureRandom $random - * @param IConfig $config - * @param EventDispatcherInterface $dispatcher - */ - public function __construct(\Doctrine\DBAL\Connection $connection, - ISecureRandom $random, - IConfig $config, - EventDispatcherInterface $dispatcher = null) { + public function __construct(Connection $connection, + IConfig $config, + ?IEventDispatcher $dispatcher = null) { $this->connection = $connection; - $this->random = $random; $this->config = $config; $this->dispatcher = $dispatcher; } /** - * @param \Doctrine\DBAL\Schema\Schema $targetSchema - * * @throws Exception */ public function migrate(Schema $targetSchema) { @@ -91,14 +48,13 @@ class Migrator { } /** - * @param \Doctrine\DBAL\Schema\Schema $targetSchema * @return string */ public function generateChangeScript(Schema $targetSchema) { $schemaDiff = $this->getDiff($targetSchema, $this->connection); $script = ''; - $sqls = $schemaDiff->toSql($this->connection->getDatabasePlatform()); + $sqls = $this->connection->getDatabasePlatform()->getAlterSchemaSQL($schemaDiff); foreach ($sqls as $sql) { $script .= $this->convertStatementToScript($sql); } @@ -107,73 +63,6 @@ class Migrator { } /** - * Create a unique name for the temporary table - * - * @param string $name - * @return string - */ - protected function generateTemporaryTableName($name) { - return $this->config->getSystemValue('dbtableprefix', 'oc_') . $name . '_' . $this->random->generate(13, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS); - } - - /** - * Check the migration of a table on a copy so we can detect errors before messing with the real table - * - * @param \Doctrine\DBAL\Schema\Table $table - * @throws \OC\DB\MigrationException - */ - protected function checkTableMigrate(Table $table) { - $name = $table->getName(); - $tmpName = $this->generateTemporaryTableName($name); - - $this->copyTable($name, $tmpName); - - //create the migration schema for the temporary table - $tmpTable = $this->renameTableSchema($table, $tmpName); - $schemaConfig = new SchemaConfig(); - $schemaConfig->setName($this->connection->getDatabase()); - $schema = new Schema([$tmpTable], [], $schemaConfig); - - try { - $this->applySchema($schema); - $this->dropTable($tmpName); - } catch (Exception $e) { - // pgsql needs to commit it's failed transaction before doing anything else - if ($this->connection->isTransactionActive()) { - $this->connection->commit(); - } - $this->dropTable($tmpName); - throw new MigrationException($table->getName(), $e->getMessage()); - } - } - - /** - * @param \Doctrine\DBAL\Schema\Table $table - * @param string $newName - * @return \Doctrine\DBAL\Schema\Table - */ - protected function renameTableSchema(Table $table, $newName) { - /** - * @var \Doctrine\DBAL\Schema\Index[] $indexes - */ - $indexes = $table->getIndexes(); - $newIndexes = []; - foreach ($indexes as $index) { - if ($index->isPrimary()) { - // do not rename primary key - $indexName = $index->getName(); - } else { - // avoid conflicts in index names - $indexName = $this->config->getSystemValue('dbtableprefix', 'oc_') . $this->random->generate(13, ISecureRandom::CHAR_LOWER); - } - $newIndexes[] = new Index($indexName, $index->getColumns(), $index->isUnique(), $index->isPrimary()); - } - - // foreign keys are not supported so we just set it to an empty array - return new Table($newName, $table->getColumns(), $newIndexes, [], [], $table->getOptions()); - } - - /** * @throws Exception */ public function createSchema() { @@ -181,24 +70,24 @@ class Migrator { /** @var string|AbstractAsset $asset */ $filterExpression = $this->getFilterExpression(); if ($asset instanceof AbstractAsset) { - return preg_match($filterExpression, $asset->getName()) !== false; + return preg_match($filterExpression, $asset->getName()) === 1; } - return preg_match($filterExpression, $asset) !== false; + return preg_match($filterExpression, $asset) === 1; }); - return $this->connection->getSchemaManager()->createSchema(); + return $this->connection->createSchemaManager()->introspectSchema(); } /** - * @param Schema $targetSchema - * @param \Doctrine\DBAL\Connection $connection - * @return \Doctrine\DBAL\Schema\SchemaDiff + * @return SchemaDiff */ - protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { - // adjust varchar columns with a length higher then getVarcharMaxLength to clob + protected function getDiff(Schema $targetSchema, Connection $connection) { + // Adjust STRING columns with a length higher than 4000 to TEXT (clob) + // for consistency between the supported databases and + // old vs. new installations. foreach ($targetSchema->getTables() as $table) { foreach ($table->getColumns() as $column) { if ($column->getType() instanceof StringType) { - if ($column->getLength() > $connection->getDatabasePlatform()->getVarcharMaxLength()) { + if ($column->getLength() > 4000) { $column->setType(Type::getType('text')); $column->setLength(null); } @@ -210,11 +99,11 @@ class Migrator { /** @var string|AbstractAsset $asset */ $filterExpression = $this->getFilterExpression(); if ($asset instanceof AbstractAsset) { - return preg_match($filterExpression, $asset->getName()) !== false; + return preg_match($filterExpression, $asset->getName()) === 1; } - return preg_match($filterExpression, $asset) !== false; + return preg_match($filterExpression, $asset) === 1; }); - $sourceSchema = $connection->getSchemaManager()->createSchema(); + $sourceSchema = $connection->createSchemaManager()->introspectSchema(); // remove tables we don't know about foreach ($sourceSchema->getTables() as $table) { @@ -229,17 +118,14 @@ class Migrator { } } - $comparator = new Comparator(); - return $comparator->compare($sourceSchema, $targetSchema); + $comparator = $connection->createSchemaManager()->createComparator(); + return $comparator->compareSchemas($sourceSchema, $targetSchema); } /** - * @param \Doctrine\DBAL\Schema\Schema $targetSchema - * @param \Doctrine\DBAL\Connection $connection - * * @throws Exception */ - protected function applySchema(Schema $targetSchema, \Doctrine\DBAL\Connection $connection = null) { + protected function applySchema(Schema $targetSchema, ?Connection $connection = null) { if (is_null($connection)) { $connection = $this->connection; } @@ -249,11 +135,11 @@ class Migrator { if (!$connection->getDatabasePlatform() instanceof MySQLPlatform) { $connection->beginTransaction(); } - $sqls = $schemaDiff->toSql($connection->getDatabasePlatform()); + $sqls = $connection->getDatabasePlatform()->getAlterSchemaSQL($schemaDiff); $step = 0; foreach ($sqls as $sql) { $this->emit($sql, $step++, count($sqls)); - $connection->query($sql); + $connection->executeStatement($sql); } if (!$connection->getDatabasePlatform() instanceof MySQLPlatform) { $connection->commit(); @@ -261,25 +147,6 @@ class Migrator { } /** - * @param string $sourceName - * @param string $targetName - */ - protected function copyTable($sourceName, $targetName) { - $quotedSource = $this->connection->quoteIdentifier($sourceName); - $quotedTarget = $this->connection->quoteIdentifier($targetName); - - $this->connection->exec('CREATE TABLE ' . $quotedTarget . ' (LIKE ' . $quotedSource . ')'); - $this->connection->exec('INSERT INTO ' . $quotedTarget . ' SELECT * FROM ' . $quotedSource); - } - - /** - * @param string $name - */ - protected function dropTable($name) { - $this->connection->exec('DROP TABLE ' . $this->connection->quoteIdentifier($name)); - } - - /** * @param $statement * @return string */ @@ -291,23 +158,16 @@ class Migrator { } protected function getFilterExpression() { - return '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/'; + return '/^' . preg_quote($this->config->getSystemValueString('dbtableprefix', 'oc_'), '/') . '/'; } - protected function emit($sql, $step, $max) { + protected function emit(string $sql, int $step, int $max): void { if ($this->noEmit) { return; } if (is_null($this->dispatcher)) { return; } - $this->dispatcher->dispatch('\OC\DB\Migrator::executeSql', new GenericEvent($sql, [$step + 1, $max])); - } - - private function emitCheckStep($tableName, $step, $max) { - if (is_null($this->dispatcher)) { - return; - } - $this->dispatcher->dispatch('\OC\DB\Migrator::checkTable', new GenericEvent($tableName, [$step + 1, $max])); + $this->dispatcher->dispatchTyped(new MigratorExecuteSqlEvent($sql, $step, $max)); } } |