From 9c6a93a87c835dee5fb0b580865d4f70836685cf Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 29 Jan 2014 15:48:12 +0100 Subject: Add a mechanism to try the database migration on a copy of the tables before running it on the "real" data --- lib/private/db/migrationexception.php | 19 +++++ lib/private/db/migrator.php | 139 ++++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 lib/private/db/migrationexception.php create mode 100644 lib/private/db/migrator.php (limited to 'lib/private/db') diff --git a/lib/private/db/migrationexception.php b/lib/private/db/migrationexception.php new file mode 100644 index 00000000000..f257534812f --- /dev/null +++ b/lib/private/db/migrationexception.php @@ -0,0 +1,19 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\DB; + + +class MigrationException extends \Exception { + private $table; + + public function __construct($table, $message) { + $this->$table = $table; + parent::__construct($message); + } +} diff --git a/lib/private/db/migrator.php b/lib/private/db/migrator.php new file mode 100644 index 00000000000..91060663f87 --- /dev/null +++ b/lib/private/db/migrator.php @@ -0,0 +1,139 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\DB; + +use \Doctrine\DBAL\DBALException; +use \Doctrine\DBAL\Schema\Table; +use \Doctrine\DBAL\Schema\Schema; +use \Doctrine\DBAL\Schema\SchemaConfig; + +class Migrator { + /** + * @var \Doctrine\DBAL\Connection $connection + */ + protected $connection; + + /** + * @param \Doctrine\DBAL\Connection $connection + */ + public function __construct(\Doctrine\DBAL\Connection $connection) { + $this->connection = $connection; + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $targetSchema + */ + public function migrate(Schema $targetSchema) { + $this->applySchema($targetSchema); + } + + /** + * @param Schema $targetSchema + */ + public function checkMigrate(Schema $targetSchema) { + /** + * @var \Doctrine\DBAL\Schema\Table[] $tables + */ + $tables = $targetSchema->getTables(); + + $existingTables = $this->connection->getSchemaManager()->listTableNames(); + + foreach ($tables as $table) { + // don't need to check for new tables + list(, $tableName) = explode('.', $table->getName()); + if (array_search($tableName, $existingTables) !== false) { + $this->checkTableMigrate($table); + } + } + } + + /** + * 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 + */ + protected function checkTableMigrate(Table $table) { + $name = $table->getName(); + $tmpName = $name . '_copy_' . uniqid(); + + $this->copyTable($name, $tmpName); + + //create the migration schema for the temporary table + $tmpTable = new Table($tmpName, $table->getColumns(), $table->getIndexes(), $table->getForeignKeys(), $table->_idGeneratorType, $table->getOptions()); + $schemaConfig = new SchemaConfig(); + $schemaConfig->setName($this->connection->getDatabase()); + $schema = new Schema(array($tmpTable), array(), $schemaConfig); + + try { + $this->applySchema($schema); + $this->dropTable($tmpName); + } catch (DBALException $e) { + $this->dropTable($tmpName); + throw new MigrationException($table->getName(), $e->getMessage()); + } + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $targetSchema + * @param \Doctrine\DBAL\Connection $connection + */ + protected function applySchema(Schema $targetSchema, \Doctrine\DBAL\Connection $connection = null) { + if (is_null($connection)) { + $connection = $this->connection; + } + + $sourceSchema = $this->connection->getSchemaManager()->createSchema(); + + // remove tables we don't know about + /** @var $table \Doctrine\DBAL\Schema\Table */ + foreach ($sourceSchema->getTables() as $table) { + if (!$targetSchema->hasTable($table->getName())) { + $sourceSchema->dropTable($table->getName()); + } + } + // remove sequences we don't know about + foreach ($sourceSchema->getSequences() as $table) { + if (!$targetSchema->hasSequence($table->getName())) { + $sourceSchema->dropSequence($table->getName()); + } + } + + $comparator = new \Doctrine\DBAL\Schema\Comparator(); + $schemaDiff = $comparator->compare($sourceSchema, $targetSchema); + + foreach ($schemaDiff->changedTables as $tableDiff) { + $tableDiff->name = $this->connection->quoteIdentifier($tableDiff->name); + } + + $connection->beginTransaction(); + foreach ($schemaDiff->toSql($connection->getDatabasePlatform()) as $sql) { + $connection->query($sql); + } + $connection->commit(); + } + + /** + * @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)); + } +} -- cgit v1.2.3 From 0035147be96537193f0c7119dfd0628fe7363a26 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 30 Jan 2014 14:21:16 +0100 Subject: Create unique names for temporary indexes --- lib/private/db/migrator.php | 26 +++++++++++++++++++++++--- tests/lib/db/migrator.php | 13 ++++++++----- 2 files changed, 31 insertions(+), 8 deletions(-) (limited to 'lib/private/db') diff --git a/lib/private/db/migrator.php b/lib/private/db/migrator.php index 91060663f87..a6c61f35424 100644 --- a/lib/private/db/migrator.php +++ b/lib/private/db/migrator.php @@ -9,9 +9,11 @@ namespace OC\DB; use \Doctrine\DBAL\DBALException; +use \Doctrine\DBAL\Schema\Index; use \Doctrine\DBAL\Schema\Table; use \Doctrine\DBAL\Schema\Schema; use \Doctrine\DBAL\Schema\SchemaConfig; +use \Doctrine\DBAL\Schema\Comparator; class Migrator { /** @@ -57,15 +59,16 @@ class Migrator { * 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 = $name . '_copy_' . uniqid(); + $tmpName = uniqid(); $this->copyTable($name, $tmpName); //create the migration schema for the temporary table - $tmpTable = new Table($tmpName, $table->getColumns(), $table->getIndexes(), $table->getForeignKeys(), $table->_idGeneratorType, $table->getOptions()); + $tmpTable = $this->renameTableSchema($table, $tmpName); $schemaConfig = new SchemaConfig(); $schemaConfig->setName($this->connection->getDatabase()); $schema = new Schema(array($tmpTable), array(), $schemaConfig); @@ -79,6 +82,23 @@ class Migrator { } } + /** + * @param \Doctrine\DBAL\Schema\Table $table + * @param string $newName + * @return \Doctrine\DBAL\Schema\Table + */ + protected function renameTableSchema(Table $table, $newName) { + $indexes = $table->getIndexes(); + $newIndexes = array(); + foreach ($indexes as $index) { + $indexName = uniqid(); // avoid conflicts in index names + $newIndexes[] = new Index($indexName, $index->getColumns(), $index->isUnique(), $index->isPrimary(), $index->getFlags()); + } + + // foreign keys are not supported so we just set it to an empty array + return new Table($newName, $table->getColumns(), $indexes, array(), 0, $table->getOptions()); + } + /** * @param \Doctrine\DBAL\Schema\Schema $targetSchema * @param \Doctrine\DBAL\Connection $connection @@ -104,7 +124,7 @@ class Migrator { } } - $comparator = new \Doctrine\DBAL\Schema\Comparator(); + $comparator = new Comparator(); $schemaDiff = $comparator->compare($sourceSchema, $targetSchema); foreach ($schemaDiff->changedTables as $tableDiff) { diff --git a/tests/lib/db/migrator.php b/tests/lib/db/migrator.php index ed7c36dbdf4..e08f1cf4547 100644 --- a/tests/lib/db/migrator.php +++ b/tests/lib/db/migrator.php @@ -20,30 +20,33 @@ class Migrator extends \PHPUnit_Framework_TestCase { private $tableName; + private $fullTableName; + public function setUp() { $this->tableName = 'test_' . uniqid(); $this->connection = \OC_DB::getConnection(); - $this->tableName = $this->connection->getDatabase() . '.' . $this->tableName; + $this->fullTableName = $this->connection->getDatabase() . '.' . $this->tableName; } public function tearDown() { - $this->connection->exec('DROP TABLE ' . $this->tableName); + $this->connection->exec('DROP TABLE ' . $this->fullTableName); } private function getInitialSchema() { $schema = new Schema(array(), array(), $this->getSchemaConfig()); - $table = $schema->createTable($this->tableName); + $table = $schema->createTable($this->fullTableName); $table->addColumn('id', 'integer'); $table->addColumn('name', 'string'); + $table->addIndex(array('id'), $this->tableName . '_id'); return $schema; } private function getNewSchema() { $schema = new Schema(array(), array(), $this->getSchemaConfig()); - $table = $schema->createTable($this->tableName); + $table = $schema->createTable($this->fullTableName); $table->addColumn('id', 'integer'); $table->addColumn('name', 'string'); - $table->addUniqueIndex(array('id')); + $table->addUniqueIndex(array('id'), $this->tableName . '_id'); return $schema; } -- cgit v1.2.3 From 58c61c833632aec7aa45eb5817fe93b420bad574 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 12 Mar 2014 14:23:00 +0100 Subject: Fix generating migration test schemas --- lib/private/db/migrator.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib/private/db') diff --git a/lib/private/db/migrator.php b/lib/private/db/migrator.php index a6c61f35424..cf95669fb83 100644 --- a/lib/private/db/migrator.php +++ b/lib/private/db/migrator.php @@ -88,11 +88,14 @@ class Migrator { * @return \Doctrine\DBAL\Schema\Table */ protected function renameTableSchema(Table $table, $newName) { + /** + * @var \Doctrine\DBAL\Schema\Index[] $indexes + */ $indexes = $table->getIndexes(); $newIndexes = array(); foreach ($indexes as $index) { $indexName = uniqid(); // avoid conflicts in index names - $newIndexes[] = new Index($indexName, $index->getColumns(), $index->isUnique(), $index->isPrimary(), $index->getFlags()); + $newIndexes[] = new Index($indexName, $index->getColumns(), $index->isUnique(), $index->isPrimary()); } // foreign keys are not supported so we just set it to an empty array -- cgit v1.2.3 From be80dce58564c88aa74fb353ab17793fc7eb37e0 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 14 Mar 2014 14:32:02 +0100 Subject: Fix temporary schema creation --- lib/private/db/migrator.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'lib/private/db') diff --git a/lib/private/db/migrator.php b/lib/private/db/migrator.php index cf95669fb83..63fddd9c28a 100644 --- a/lib/private/db/migrator.php +++ b/lib/private/db/migrator.php @@ -47,8 +47,12 @@ class Migrator { $existingTables = $this->connection->getSchemaManager()->listTableNames(); foreach ($tables as $table) { + if (strpos($table->getName(), '.')) { + list(, $tableName) = explode('.', $table->getName()); + } else { + $tableName = $table->getName(); + } // don't need to check for new tables - list(, $tableName) = explode('.', $table->getName()); if (array_search($tableName, $existingTables) !== false) { $this->checkTableMigrate($table); } @@ -63,7 +67,7 @@ class Migrator { */ protected function checkTableMigrate(Table $table) { $name = $table->getName(); - $tmpName = uniqid(); + $tmpName = 'oc_' . uniqid(); $this->copyTable($name, $tmpName); @@ -94,12 +98,12 @@ class Migrator { $indexes = $table->getIndexes(); $newIndexes = array(); foreach ($indexes as $index) { - $indexName = uniqid(); // avoid conflicts in index names + $indexName = 'oc_' . uniqid(); // avoid conflicts in index names $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(), $indexes, array(), 0, $table->getOptions()); + return new Table($newName, $table->getColumns(), $newIndexes, array(), 0, $table->getOptions()); } /** -- cgit v1.2.3 From 35550e8d9a7239c66457fc1533462fd5949d9d2d Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 14 Mar 2014 16:28:56 +0100 Subject: Fix migrator for postgres --- lib/private/db/migrator.php | 4 +++- tests/lib/db/migrator.php | 10 +++------- 2 files changed, 6 insertions(+), 8 deletions(-) (limited to 'lib/private/db') diff --git a/lib/private/db/migrator.php b/lib/private/db/migrator.php index 63fddd9c28a..3f32e8c603e 100644 --- a/lib/private/db/migrator.php +++ b/lib/private/db/migrator.php @@ -81,6 +81,8 @@ class Migrator { $this->applySchema($schema); $this->dropTable($tmpName); } catch (DBALException $e) { + // pgsql needs to commit it's failed transaction before doing anything else + $this->connection->commit(); $this->dropTable($tmpName); throw new MigrationException($table->getName(), $e->getMessage()); } @@ -153,7 +155,7 @@ class Migrator { $quotedSource = $this->connection->quoteIdentifier($sourceName); $quotedTarget = $this->connection->quoteIdentifier($targetName); - $this->connection->exec('CREATE TABLE ' . $quotedTarget . ' LIKE ' . $quotedSource); + $this->connection->exec('CREATE TABLE ' . $quotedTarget . ' (LIKE ' . $quotedSource . ')'); $this->connection->exec('INSERT INTO ' . $quotedTarget . ' SELECT * FROM ' . $quotedSource); } diff --git a/tests/lib/db/migrator.php b/tests/lib/db/migrator.php index 258d6451847..88f6d7dcc63 100644 --- a/tests/lib/db/migrator.php +++ b/tests/lib/db/migrator.php @@ -20,25 +20,21 @@ class Migrator extends \PHPUnit_Framework_TestCase { private $tableName; - private $fullTableName; - public function setUp() { $this->tableName = 'test_' . uniqid(); $this->connection = \OC_DB::getConnection(); if ($this->connection->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver) { $this->markTestSkipped('Migration tests dont function on sqlite since sqlite doesnt give an error on existing duplicate data'); - } else { - $this->fullTableName = $this->connection->getDatabase() . '.' . $this->tableName; } } public function tearDown() { - $this->connection->exec('DROP TABLE ' . $this->fullTableName); + $this->connection->exec('DROP TABLE ' . $this->tableName); } private function getInitialSchema() { $schema = new Schema(array(), array(), $this->getSchemaConfig()); - $table = $schema->createTable($this->fullTableName); + $table = $schema->createTable($this->tableName); $table->addColumn('id', 'integer'); $table->addColumn('name', 'string'); $table->addIndex(array('id'), $this->tableName . '_id'); @@ -47,7 +43,7 @@ class Migrator extends \PHPUnit_Framework_TestCase { private function getNewSchema() { $schema = new Schema(array(), array(), $this->getSchemaConfig()); - $table = $schema->createTable($this->fullTableName); + $table = $schema->createTable($this->tableName); $table->addColumn('id', 'integer'); $table->addColumn('name', 'string'); $table->addUniqueIndex(array('id'), $this->tableName . '_id'); -- cgit v1.2.3 From adeac7aa3925c76dc53fb011c873d25eae520a4e Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 27 Mar 2014 14:44:19 +0100 Subject: Check sqlite migration on a copy of the database file --- lib/private/db/migrator.php | 1 + lib/private/db/sqlitemigrator.php | 40 ++++++++++++++++++++++++ tests/lib/db/migrator.php | 66 ++++++++++++++++++++++++++++----------- 3 files changed, 88 insertions(+), 19 deletions(-) create mode 100644 lib/private/db/sqlitemigrator.php (limited to 'lib/private/db') diff --git a/lib/private/db/migrator.php b/lib/private/db/migrator.php index 3f32e8c603e..5ba16e34311 100644 --- a/lib/private/db/migrator.php +++ b/lib/private/db/migrator.php @@ -37,6 +37,7 @@ class Migrator { /** * @param Schema $targetSchema + * @throws \OC\DB\MigrationException */ public function checkMigrate(Schema $targetSchema) { /** diff --git a/lib/private/db/sqlitemigrator.php b/lib/private/db/sqlitemigrator.php new file mode 100644 index 00000000000..f5f78986771 --- /dev/null +++ b/lib/private/db/sqlitemigrator.php @@ -0,0 +1,40 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\DB; + +use Doctrine\DBAL\DBALException; + +class SQLiteMigrator extends Migrator { + /** + * @param \Doctrine\DBAL\Schema\Schema $targetSchema + * @throws \OC\DB\MigrationException + * + * For sqlite we simple make a copy of the entire database, and test the migration on that + */ + public function checkMigrate(\Doctrine\DBAL\Schema\Schema $targetSchema) { + $dbFile = $this->connection->getDatabase(); + $tmpFile = \OC_Helper::tmpFile('.db'); + copy($dbFile, $tmpFile); + + $connectionParams = array( + 'path' => $tmpFile, + 'driver' => 'pdo_sqlite', + ); + $conn = \Doctrine\DBAL\DriverManager::getConnection($connectionParams); + try { + $this->applySchema($targetSchema, $conn); + $conn->close(); + unlink($tmpFile); + } catch (DBALException $e) { + $conn->close(); + unlink($tmpFile); + throw new MigrationException('', $e->getMessage()); + } + } +} diff --git a/tests/lib/db/migrator.php b/tests/lib/db/migrator.php index 88f6d7dcc63..31dc1c4951a 100644 --- a/tests/lib/db/migrator.php +++ b/tests/lib/db/migrator.php @@ -9,6 +9,7 @@ namespace Test\DB; +use \Doctrine\DBAL\DBALException; use \Doctrine\DBAL\Schema\Schema; use \Doctrine\DBAL\Schema\SchemaConfig; @@ -23,31 +24,29 @@ class Migrator extends \PHPUnit_Framework_TestCase { public function setUp() { $this->tableName = 'test_' . uniqid(); $this->connection = \OC_DB::getConnection(); - if ($this->connection->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver) { - $this->markTestSkipped('Migration tests dont function on sqlite since sqlite doesnt give an error on existing duplicate data'); - } } public function tearDown() { $this->connection->exec('DROP TABLE ' . $this->tableName); } - private function getInitialSchema() { - $schema = new Schema(array(), array(), $this->getSchemaConfig()); - $table = $schema->createTable($this->tableName); + /** + * @return \Doctrine\DBAL\Schema\Schema[] + */ + private function getDuplicateKeySchemas() { + $startSchema = new Schema(array(), array(), $this->getSchemaConfig()); + $table = $startSchema->createTable($this->tableName); $table->addColumn('id', 'integer'); $table->addColumn('name', 'string'); $table->addIndex(array('id'), $this->tableName . '_id'); - return $schema; - } - private function getNewSchema() { - $schema = new Schema(array(), array(), $this->getSchemaConfig()); - $table = $schema->createTable($this->tableName); + $endSchema = new Schema(array(), array(), $this->getSchemaConfig()); + $table = $endSchema->createTable($this->tableName); $table->addColumn('id', 'integer'); $table->addColumn('name', 'string'); $table->addUniqueIndex(array('id'), $this->tableName . '_id'); - return $schema; + + return array($startSchema, $endSchema); } private function getSchemaConfig() { @@ -56,36 +55,65 @@ class Migrator extends \PHPUnit_Framework_TestCase { return $config; } + private function isSQLite() { + return $this->connection->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver; + } + private function getMigrator() { - return new \OC\DB\Migrator($this->connection); + if ($this->isSQLite()) { + return new \OC\DB\SQLiteMigrator($this->connection); + } else { + return new \OC\DB\Migrator($this->connection); + } } /** * @expectedException \OC\DB\MigrationException */ public function testDuplicateKeyUpgrade() { + if ($this->isSQLite()) { + $this->markTestSkipped('sqlite doesnt throw errors when creating a new key on existing data'); + } + list($startSchema, $endSchema) = $this->getDuplicateKeySchemas(); $migrator = $this->getMigrator(); - $migrator->migrate($this->getInitialSchema()); + $migrator->migrate($startSchema); $this->connection->insert($this->tableName, array('id' => 1, 'name' => 'foo')); $this->connection->insert($this->tableName, array('id' => 2, 'name' => 'bar')); $this->connection->insert($this->tableName, array('id' => 2, 'name' => 'qwerty')); - $migrator->checkMigrate($this->getNewSchema()); + $migrator->checkMigrate($endSchema); $this->fail('checkMigrate should have failed'); } public function testUpgrade() { + list($startSchema, $endSchema) = $this->getDuplicateKeySchemas(); $migrator = $this->getMigrator(); - $migrator->migrate($this->getInitialSchema()); + $migrator->migrate($startSchema); $this->connection->insert($this->tableName, array('id' => 1, 'name' => 'foo')); $this->connection->insert($this->tableName, array('id' => 2, 'name' => 'bar')); $this->connection->insert($this->tableName, array('id' => 3, 'name' => 'qwerty')); - $newSchema = $this->getNewSchema(); - $migrator->checkMigrate($newSchema); - $migrator->migrate($newSchema); + $migrator->checkMigrate($endSchema); + $migrator->migrate($endSchema); $this->assertTrue(true); } + + public function testInsertAfterUpgrade() { + list($startSchema, $endSchema) = $this->getDuplicateKeySchemas(); + $migrator = $this->getMigrator(); + $migrator->migrate($startSchema); + + $migrator->migrate($endSchema); + + $this->connection->insert($this->tableName, array('id' => 1, 'name' => 'foo')); + $this->connection->insert($this->tableName, array('id' => 2, 'name' => 'bar')); + try { + $this->connection->insert($this->tableName, array('id' => 2, 'name' => 'qwerty')); + $this->fail('Expected duplicate key insert to fail'); + } catch (DBALException $e) { + $this->assertTrue(true); + } + } } -- cgit v1.2.3 From 3b4555ca918f549d48d55b6f01a641bc0a21850e Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 8 Apr 2014 16:01:08 +0200 Subject: Try and check migration before applying it --- lib/private/db/mdb2schemamanager.php | 79 +++++++++++++----------------------- lib/private/db/migrator.php | 46 ++++++++++++++++----- 2 files changed, 65 insertions(+), 60 deletions(-) (limited to 'lib/private/db') diff --git a/lib/private/db/mdb2schemamanager.php b/lib/private/db/mdb2schemamanager.php index 4208dbd18f4..0eec4d4d317 100644 --- a/lib/private/db/mdb2schemamanager.php +++ b/lib/private/db/mdb2schemamanager.php @@ -8,6 +8,10 @@ namespace OC\DB; +use Doctrine\DBAL\Platforms\MySqlPlatform; +use Doctrine\DBAL\Platforms\PostgreSqlPlatform; +use Doctrine\DBAL\Platforms\SqlitePlatform; + class MDB2SchemaManager { /** * @var \OC\DB\Connection $conn @@ -31,7 +35,7 @@ class MDB2SchemaManager { * * TODO: write more documentation */ - public function getDbStructure( $file, $mode = MDB2_SCHEMA_DUMP_STRUCTURE) { + public function getDbStructure($file, $mode = MDB2_SCHEMA_DUMP_STRUCTURE) { $sm = $this->conn->getSchemaManager(); return \OC_DB_MDB2SchemaWriter::saveSchemaToFile($file, $sm); @@ -44,7 +48,7 @@ class MDB2SchemaManager { * * TODO: write more documentation */ - public function createDbFromStructure( $file ) { + public function createDbFromStructure($file) { $schemaReader = new MDB2SchemaReader(\OC_Config::getObject(), $this->conn->getDatabasePlatform()); $toSchema = $schemaReader->loadSchemaFromFile($file); return $this->executeSchemaChange($toSchema); @@ -53,49 +57,40 @@ 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 * @return string|boolean */ public function updateDbFromStructure($file, $generateSql = false) { - $sm = $this->conn->getSchemaManager(); - $fromSchema = $sm->createSchema(); - - $schemaReader = new MDB2SchemaReader(\OC_Config::getObject(), $this->conn->getDatabasePlatform()); - $toSchema = $schemaReader->loadSchemaFromFile($file); - - // remove tables we don't know about - /** @var $table \Doctrine\DBAL\Schema\Table */ - foreach($fromSchema->getTables() as $table) { - if (!$toSchema->hasTable($table->getName())) { - $fromSchema->dropTable($table->getName()); - } - } - // remove sequences we don't know about - foreach($fromSchema->getSequences() as $table) { - if (!$toSchema->hasSequence($table->getName())) { - $fromSchema->dropSequence($table->getName()); - } - } - - $comparator = new \Doctrine\DBAL\Schema\Comparator(); - $schemaDiff = $comparator->compare($fromSchema, $toSchema); $platform = $this->conn->getDatabasePlatform(); - foreach($schemaDiff->changedTables as $tableDiff) { - $tableDiff->name = $platform->quoteIdentifier($tableDiff->name); - foreach($tableDiff->changedColumns as $column) { - $column->oldColumnName = $platform->quoteIdentifier($column->oldColumnName); - } + $schemaReader = new MDB2SchemaReader(\OC_Config::getObject(), $platform); + $toSchema = $schemaReader->loadSchemaFromFile($file); + if ($platform instanceof SqlitePlatform) { + $check = true; + $migrator = new SQLiteMigrator($this->conn); + } else if ($platform instanceof MySqlPlatform or $platform instanceof PostgreSqlPlatform) { + $check = true; + $migrator = new Migrator($this->conn); + } else { + // dont do the upgrade check for oracle + $check = false; + $migrator = new Migrator($this->conn); } if ($generateSql) { - return $this->generateChangeScript($schemaDiff); + return $migrator->generateChangeScript($toSchema); + } else { + if ($check) { + $migrator->checkMigrate($toSchema); + } + $migrator->migrate($toSchema); + return true; } - - return $this->executeSchemaChange($schemaDiff); } /** * remove all tables defined in a database structure xml file + * * @param string $file the xml file describing the tables */ public function removeDBStructure($file) { @@ -103,7 +98,7 @@ class MDB2SchemaManager { $fromSchema = $schemaReader->loadSchemaFromFile($file); $toSchema = clone $fromSchema; /** @var $table \Doctrine\DBAL\Schema\Table */ - foreach($toSchema->getTables() as $table) { + foreach ($toSchema->getTables() as $table) { $toSchema->dropTable($table->getName()); } $comparator = new \Doctrine\DBAL\Schema\Comparator(); @@ -117,26 +112,10 @@ class MDB2SchemaManager { */ private function executeSchemaChange($schema) { $this->conn->beginTransaction(); - foreach($schema->toSql($this->conn->getDatabasePlatform()) as $sql) { + foreach ($schema->toSql($this->conn->getDatabasePlatform()) as $sql) { $this->conn->query($sql); } $this->conn->commit(); return true; } - - /** - * @param \Doctrine\DBAL\Schema\Schema $schema - * @return string - */ - public function generateChangeScript($schema) { - - $script = ''; - $sqls = $schema->toSql($this->conn->getDatabasePlatform()); - foreach($sqls as $sql) { - $script .= $sql . ';'; - $script .= PHP_EOL; - } - - return $script; - } } diff --git a/lib/private/db/migrator.php b/lib/private/db/migrator.php index 5ba16e34311..917f92f64b6 100644 --- a/lib/private/db/migrator.php +++ b/lib/private/db/migrator.php @@ -35,6 +35,23 @@ class Migrator { $this->applySchema($targetSchema); } + /** + * @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()); + foreach ($sqls as $sql) { + $script .= $sql . ';'; + $script .= PHP_EOL; + } + + return $script; + } + /** * @param Schema $targetSchema * @throws \OC\DB\MigrationException @@ -109,16 +126,8 @@ class Migrator { return new Table($newName, $table->getColumns(), $newIndexes, array(), 0, $table->getOptions()); } - /** - * @param \Doctrine\DBAL\Schema\Schema $targetSchema - * @param \Doctrine\DBAL\Connection $connection - */ - protected function applySchema(Schema $targetSchema, \Doctrine\DBAL\Connection $connection = null) { - if (is_null($connection)) { - $connection = $this->connection; - } - - $sourceSchema = $this->connection->getSchemaManager()->createSchema(); + protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { + $sourceSchema = $connection->getSchemaManager()->createSchema(); // remove tables we don't know about /** @var $table \Doctrine\DBAL\Schema\Table */ @@ -139,8 +148,25 @@ class Migrator { foreach ($schemaDiff->changedTables as $tableDiff) { $tableDiff->name = $this->connection->quoteIdentifier($tableDiff->name); + foreach ($tableDiff->changedColumns as $column) { + $column->oldColumnName = $this->connection->quoteIdentifier($column->oldColumnName); + } + } + + return $schemaDiff; + } + + /** + * @param \Doctrine\DBAL\Schema\Schema $targetSchema + * @param \Doctrine\DBAL\Connection $connection + */ + protected function applySchema(Schema $targetSchema, \Doctrine\DBAL\Connection $connection = null) { + if (is_null($connection)) { + $connection = $this->connection; } + $schemaDiff = $this->getDiff($targetSchema, $connection); + $connection->beginTransaction(); foreach ($schemaDiff->toSql($connection->getDatabasePlatform()) as $sql) { $connection->query($sql); -- cgit v1.2.3 From a59f6818ebeffa23ad1b66652179f3592bd4931d Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 11 Apr 2014 15:10:09 +0200 Subject: Only quote identifiers for oracle during migration --- lib/private/db/mdb2schemamanager.php | 33 ++++++++++++++--------------- lib/private/db/migrator.php | 11 +--------- lib/private/db/oraclemigrator.php | 40 ++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 26 deletions(-) create mode 100644 lib/private/db/oraclemigrator.php (limited to 'lib/private/db') diff --git a/lib/private/db/mdb2schemamanager.php b/lib/private/db/mdb2schemamanager.php index 0eec4d4d317..1c9b6b9d00b 100644 --- a/lib/private/db/mdb2schemamanager.php +++ b/lib/private/db/mdb2schemamanager.php @@ -8,8 +8,7 @@ namespace OC\DB; -use Doctrine\DBAL\Platforms\MySqlPlatform; -use Doctrine\DBAL\Platforms\PostgreSqlPlatform; +use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Platforms\SqlitePlatform; class MDB2SchemaManager { @@ -54,6 +53,20 @@ class MDB2SchemaManager { return $this->executeSchemaChange($toSchema); } + /** + * @return \OC\DB\Migrator + */ + protected function getMigrator() { + $platform = $this->conn->getDatabasePlatform(); + if ($platform instanceof SqlitePlatform) { + return new SQLiteMigrator($this->conn); + } else if ($platform instanceof OraclePlatform) { + return new OracleMigrator($this->conn); + } else { + return new Migrator($this->conn); + } + } + /** * update the database scheme * @param string $file file to read structure from @@ -65,24 +78,12 @@ class MDB2SchemaManager { $platform = $this->conn->getDatabasePlatform(); $schemaReader = new MDB2SchemaReader(\OC_Config::getObject(), $platform); $toSchema = $schemaReader->loadSchemaFromFile($file); - if ($platform instanceof SqlitePlatform) { - $check = true; - $migrator = new SQLiteMigrator($this->conn); - } else if ($platform instanceof MySqlPlatform or $platform instanceof PostgreSqlPlatform) { - $check = true; - $migrator = new Migrator($this->conn); - } else { - // dont do the upgrade check for oracle - $check = false; - $migrator = new Migrator($this->conn); - } + $migrator = $this->getMigrator(); if ($generateSql) { return $migrator->generateChangeScript($toSchema); } else { - if ($check) { - $migrator->checkMigrate($toSchema); - } + $migrator->checkMigrate($toSchema); $migrator->migrate($toSchema); return true; } diff --git a/lib/private/db/migrator.php b/lib/private/db/migrator.php index 917f92f64b6..5dd7735dfc6 100644 --- a/lib/private/db/migrator.php +++ b/lib/private/db/migrator.php @@ -144,16 +144,7 @@ class Migrator { } $comparator = new Comparator(); - $schemaDiff = $comparator->compare($sourceSchema, $targetSchema); - - foreach ($schemaDiff->changedTables as $tableDiff) { - $tableDiff->name = $this->connection->quoteIdentifier($tableDiff->name); - foreach ($tableDiff->changedColumns as $column) { - $column->oldColumnName = $this->connection->quoteIdentifier($column->oldColumnName); - } - } - - return $schemaDiff; + return $comparator->compare($sourceSchema, $targetSchema); } /** diff --git a/lib/private/db/oraclemigrator.php b/lib/private/db/oraclemigrator.php new file mode 100644 index 00000000000..5494bcbac30 --- /dev/null +++ b/lib/private/db/oraclemigrator.php @@ -0,0 +1,40 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\DB; + +use Doctrine\DBAL\Schema\Schema; + +class OracleMigrator extends Migrator { + /** + * @param \Doctrine\DBAL\Schema\Schema $targetSchema + * @throws \OC\DB\MigrationException + * + * Migration testing is skipped for oracle + */ + public function checkMigrate(Schema $targetSchema) {} + + /** + * @param Schema $targetSchema + * @param \Doctrine\DBAL\Connection $connection + * @return \Doctrine\DBAL\Schema\SchemaDiff + */ + protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { + $schemaDiff = parent::getDiff($targetSchema, $connection); + + // oracle forces us to quote the identifiers + foreach ($schemaDiff->changedTables as $tableDiff) { + $tableDiff->name = $this->connection->quoteIdentifier($tableDiff->name); + foreach ($tableDiff->changedColumns as $column) { + $column->oldColumnName = $this->connection->quoteIdentifier($column->oldColumnName); + } + } + + return $schemaDiff; + } +} -- cgit v1.2.3 From 6f71419f2b0e8e671274ca761e636ebe0a4067b3 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 6 May 2014 14:04:22 +0200 Subject: Skip migration checks for all sql backends besides mysql, postgres and sqlite --- lib/private/db/mdb2schemamanager.php | 6 +++++- lib/private/db/nocheckmigrator.php | 24 ++++++++++++++++++++++++ lib/private/db/oraclemigrator.php | 10 +--------- 3 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 lib/private/db/nocheckmigrator.php (limited to 'lib/private/db') diff --git a/lib/private/db/mdb2schemamanager.php b/lib/private/db/mdb2schemamanager.php index 1c9b6b9d00b..1a19e737da8 100644 --- a/lib/private/db/mdb2schemamanager.php +++ b/lib/private/db/mdb2schemamanager.php @@ -8,7 +8,9 @@ namespace OC\DB; +use Doctrine\DBAL\Platforms\MySqlPlatform; use Doctrine\DBAL\Platforms\OraclePlatform; +use Doctrine\DBAL\Platforms\PostgreSqlPlatform; use Doctrine\DBAL\Platforms\SqlitePlatform; class MDB2SchemaManager { @@ -62,8 +64,10 @@ class MDB2SchemaManager { return new SQLiteMigrator($this->conn); } else if ($platform instanceof OraclePlatform) { return new OracleMigrator($this->conn); - } else { + } else if ($platform instanceof MySqlPlatform or $platform instanceof PostgreSqlPlatform) { return new Migrator($this->conn); + } else { + return new NoCheckMigrator($this->conn); } } diff --git a/lib/private/db/nocheckmigrator.php b/lib/private/db/nocheckmigrator.php new file mode 100644 index 00000000000..cd2b47c214a --- /dev/null +++ b/lib/private/db/nocheckmigrator.php @@ -0,0 +1,24 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\DB; + +use Doctrine\DBAL\Schema\Schema; + +/** + * migrator for database platforms that don't support the upgrade check + * + * @package OC\DB + */ +class NoCheckMigrator extends Migrator { + /** + * @param \Doctrine\DBAL\Schema\Schema $targetSchema + * @throws \OC\DB\MigrationException + */ + public function checkMigrate(Schema $targetSchema) {} +} diff --git a/lib/private/db/oraclemigrator.php b/lib/private/db/oraclemigrator.php index 5494bcbac30..de4348bd56d 100644 --- a/lib/private/db/oraclemigrator.php +++ b/lib/private/db/oraclemigrator.php @@ -10,15 +10,7 @@ namespace OC\DB; use Doctrine\DBAL\Schema\Schema; -class OracleMigrator extends Migrator { - /** - * @param \Doctrine\DBAL\Schema\Schema $targetSchema - * @throws \OC\DB\MigrationException - * - * Migration testing is skipped for oracle - */ - public function checkMigrate(Schema $targetSchema) {} - +class OracleMigrator extends NoCheckMigrator { /** * @param Schema $targetSchema * @param \Doctrine\DBAL\Connection $connection -- cgit v1.2.3 From c6053b283046c7db7201d60d341e279032394002 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 6 May 2014 14:46:59 +0200 Subject: Quote identifiers on mysql --- lib/private/db/mdb2schemamanager.php | 4 +++- lib/private/db/mysqlmigrator.php | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 lib/private/db/mysqlmigrator.php (limited to 'lib/private/db') diff --git a/lib/private/db/mdb2schemamanager.php b/lib/private/db/mdb2schemamanager.php index 1a19e737da8..d6e0e77b54e 100644 --- a/lib/private/db/mdb2schemamanager.php +++ b/lib/private/db/mdb2schemamanager.php @@ -64,7 +64,9 @@ class MDB2SchemaManager { return new SQLiteMigrator($this->conn); } else if ($platform instanceof OraclePlatform) { return new OracleMigrator($this->conn); - } else if ($platform instanceof MySqlPlatform or $platform instanceof PostgreSqlPlatform) { + } else if ($platform instanceof MySqlPlatform) { + return new MySQLMigrator($this->conn); + } else if ($platform instanceof PostgreSqlPlatform) { return new Migrator($this->conn); } else { return new NoCheckMigrator($this->conn); diff --git a/lib/private/db/mysqlmigrator.php b/lib/private/db/mysqlmigrator.php new file mode 100644 index 00000000000..97495f52032 --- /dev/null +++ b/lib/private/db/mysqlmigrator.php @@ -0,0 +1,32 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\DB; + +use Doctrine\DBAL\Schema\Schema; + +class MySQLMigrator extends Migrator { + /** + * @param Schema $targetSchema + * @param \Doctrine\DBAL\Connection $connection + * @return \Doctrine\DBAL\Schema\SchemaDiff + */ + protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { + $schemaDiff = parent::getDiff($targetSchema, $connection); + + // identifiers need to be quoted for mysql + foreach ($schemaDiff->changedTables as $tableDiff) { + $tableDiff->name = $this->connection->quoteIdentifier($tableDiff->name); + foreach ($tableDiff->changedColumns as $column) { + $column->oldColumnName = $this->connection->quoteIdentifier($column->oldColumnName); + } + } + + return $schemaDiff; + } +} -- cgit v1.2.3 From 397a763c49ea8b9585405e711f92969191c0c613 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 7 May 2014 12:53:42 +0200 Subject: add a getter for the table --- lib/private/db/migrationexception.php | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'lib/private/db') diff --git a/lib/private/db/migrationexception.php b/lib/private/db/migrationexception.php index f257534812f..f3c908aa40a 100644 --- a/lib/private/db/migrationexception.php +++ b/lib/private/db/migrationexception.php @@ -16,4 +16,11 @@ class MigrationException extends \Exception { $this->$table = $table; parent::__construct($message); } + + /** + * @return string + */ + public function getTable() { + return $this->table; + } } -- cgit v1.2.3 From 3be2643168a47813718d24232ee25e9b8f264b7a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 26 May 2014 15:59:02 +0200 Subject: Add `generateChangeScript()` back --- lib/private/db/mdb2schemamanager.php | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'lib/private/db') diff --git a/lib/private/db/mdb2schemamanager.php b/lib/private/db/mdb2schemamanager.php index d6e0e77b54e..533ed9110e7 100644 --- a/lib/private/db/mdb2schemamanager.php +++ b/lib/private/db/mdb2schemamanager.php @@ -95,6 +95,15 @@ class MDB2SchemaManager { } } + /** + * @param \Doctrine\DBAL\Schema\Schema $schema + * @return string + */ + public function generateChangeScript($schema) { + $migrator = $this->getMigrator(); + return $migrator->generateChangeScript($schema); + } + /** * remove all tables defined in a database structure xml file * -- cgit v1.2.3 From 82b982a4ebe42e98b800d58ec98c307d531d71f9 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 3 Jun 2014 11:24:31 +0200 Subject: Better unique names for temporary tables --- lib/private/db/migrator.php | 12 +++++++++++- lib/private/db/oraclemigrator.php | 8 ++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'lib/private/db') diff --git a/lib/private/db/migrator.php b/lib/private/db/migrator.php index 5dd7735dfc6..517be8399e8 100644 --- a/lib/private/db/migrator.php +++ b/lib/private/db/migrator.php @@ -77,6 +77,16 @@ class Migrator { } } + /** + * Create a unique name for the temporary table + * + * @param string $name + * @return string + */ + protected function generateTemporaryTableName($name) { + return 'oc_' . $name . '_' . uniqid(); + } + /** * Check the migration of a table on a copy so we can detect errors before messing with the real table * @@ -85,7 +95,7 @@ class Migrator { */ protected function checkTableMigrate(Table $table) { $name = $table->getName(); - $tmpName = 'oc_' . uniqid(); + $tmpName = $this->generateTemporaryTableName($name); $this->copyTable($name, $tmpName); diff --git a/lib/private/db/oraclemigrator.php b/lib/private/db/oraclemigrator.php index de4348bd56d..1a8df2def9c 100644 --- a/lib/private/db/oraclemigrator.php +++ b/lib/private/db/oraclemigrator.php @@ -29,4 +29,12 @@ class OracleMigrator extends NoCheckMigrator { return $schemaDiff; } + + /** + * @param string $name + * @return string + */ + protected function generateTemporaryTableName($name) { + return 'oc_' . uniqid(); + } } -- cgit v1.2.3 From 4429b54ce4fdf5aa2936dd733f5c1751d342177e Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 3 Jun 2014 12:00:39 +0200 Subject: Fix typo --- lib/private/db/migrationexception.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/private/db') diff --git a/lib/private/db/migrationexception.php b/lib/private/db/migrationexception.php index f3c908aa40a..2afec9700a0 100644 --- a/lib/private/db/migrationexception.php +++ b/lib/private/db/migrationexception.php @@ -13,7 +13,7 @@ class MigrationException extends \Exception { private $table; public function __construct($table, $message) { - $this->$table = $table; + $this->table = $table; parent::__construct($message); } -- cgit v1.2.3