|
|
|
@ -35,14 +35,10 @@ 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\Types\StringType; |
|
|
|
|
use Doctrine\DBAL\Types\Type; |
|
|
|
|
use OCP\IConfig; |
|
|
|
|
use OCP\Security\ISecureRandom; |
|
|
|
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
|
|
|
|
use Symfony\Component\EventDispatcher\GenericEvent; |
|
|
|
|
use function preg_match; |
|
|
|
@ -52,9 +48,6 @@ class Migrator { |
|
|
|
|
/** @var \Doctrine\DBAL\Connection */ |
|
|
|
|
protected $connection; |
|
|
|
|
|
|
|
|
|
/** @var ISecureRandom */ |
|
|
|
|
private $random; |
|
|
|
|
|
|
|
|
|
/** @var IConfig */ |
|
|
|
|
protected $config; |
|
|
|
|
|
|
|
|
@ -66,16 +59,13 @@ class Migrator { |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @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) { |
|
|
|
|
$this->connection = $connection; |
|
|
|
|
$this->random = $random; |
|
|
|
|
$this->config = $config; |
|
|
|
|
$this->dispatcher = $dispatcher; |
|
|
|
|
} |
|
|
|
@ -106,73 +96,6 @@ class Migrator { |
|
|
|
|
return $script; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 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 |
|
|
|
|
*/ |
|
|
|
@ -260,25 +183,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 |
|
|
|
@ -303,11 +207,4 @@ class Migrator { |
|
|
|
|
} |
|
|
|
|
$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])); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|