diff --git a/lib/private/DB/ConnectionFactory.php b/lib/private/DB/ConnectionFactory.php index d592d427f0e4..91fc6ec598b9 100644 --- a/lib/private/DB/ConnectionFactory.php +++ b/lib/private/DB/ConnectionFactory.php @@ -129,6 +129,10 @@ public function getConnection($type, $additionalConnectionParams) { $additionalConnectionParams['platform'] = new OCSqlitePlatform(); $eventManager->addEventSubscriber(new SQLiteSessionInit(true, $journalMode)); break; + case 'pgsql': + case 'postgresql': + $additionalConnectionParams['platform'] = new OCPostgreSqlPlatform(); + break; } /** @var Connection $connection */ $connection = DriverManager::getConnection( diff --git a/lib/private/DB/OCPostgreSqlPlatform.php b/lib/private/DB/OCPostgreSqlPlatform.php new file mode 100644 index 000000000000..44ca929402e6 --- /dev/null +++ b/lib/private/DB/OCPostgreSqlPlatform.php @@ -0,0 +1,86 @@ + + * + * @copyright Copyright (c) 2017, 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\Schema\TableDiff; +use Doctrine\DBAL\Types\Type; + +class OCPostgreSqlPlatform extends \Doctrine\DBAL\Platforms\PostgreSqlPlatform { + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff){ + $sqls = parent::getAlterTableSQL($diff); + foreach ($sqls as $index => $sql){ + // BIGSERIAL could not be used in statements altering column type + // That's why we replace it with BIGINT + // see https://github.com/owncloud/core/pull/28364#issuecomment-315006853 + if (preg_match('|(ALTER TABLE\s+\S+\s+ALTER\s+\S+\s+TYPE\s+)(BIGSERIAL)|i', $sql, $matches)){ + $alterTable = $matches[1]; + $sqls[$index] = $alterTable . 'BIGINT'; + } + // Changing integer to bigint kills next autoincrement value + // see https://github.com/owncloud/core/pull/28364#issuecomment-315006853 + if (preg_match('|ALTER TABLE\s+(\S+)\s+ALTER\s+(\S+)\s+DROP DEFAULT|i', $sql, $matches)){ + $queryTableName = $matches[1]; + $queryColumnName = $matches[2]; + $columnDiff = $this->findColumnDiffByName($diff, $queryColumnName); + if ($columnDiff){ + if ($this->shouldSkipDropDefault($columnDiff)){ + unset($sqls[$index]); + continue; + } + } + } + } + + return $sqls; + } + + /** + * We should NOT drop next seqence value if + * - type was changed from INTEGER to BIGINT + * - column keeps an autoincrement + * - default value is kept NULL + */ + private function shouldSkipDropDefault($columnDiff){ + $column = $columnDiff->column; + $fromColumn = $columnDiff->fromColumn; + return $fromColumn->getType()->getName() === Type::INTEGER + && $column->getType()->getName() === Type::BIGINT + && is_null($fromColumn->getDefault()) + && is_null($column->getDefault()) + && $fromColumn->getAutoincrement() + && $column->getAutoincrement(); + } + + private function findColumnDiffByName($diff, $name){ + foreach ($diff->changedColumns as $columnDiff){ + $oldColumnName = $columnDiff->getOldColumnName()->getQuotedName($this); + if ($oldColumnName === $name){ + return $columnDiff; + } + return false; + } + } +} diff --git a/tests/lib/DB/OCPostgreSqlPlatformTest.php b/tests/lib/DB/OCPostgreSqlPlatformTest.php new file mode 100644 index 000000000000..56fab621cfc4 --- /dev/null +++ b/tests/lib/DB/OCPostgreSqlPlatformTest.php @@ -0,0 +1,74 @@ + + * + * @copyright Copyright (c) 2017, 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 Test\DB; + +use Doctrine\DBAL\Schema\Comparator; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Types\Type; +use OC\DB\OCPostgreSqlPlatform; + + /** + * Class OCPostgreSqlPlatformTest + * + * @group DB + * + * @package Test\DB + */ + +class OCPostgreSqlPlatformTest extends \Test\TestCase { + + public function testAlterBigint(){ + $platform = new OCPostgreSqlPlatform(); + $sourceSchema = new Schema(); + $targetSchema = new Schema(); + + $this->createTableAndColumn($sourceSchema, Type::INTEGER); + $this->createTableAndColumn($targetSchema, Type::BIGINT); + + $comparator = new Comparator(); + $diff = $comparator->compare($sourceSchema, $targetSchema); + $sqlStatements = $diff->toSql($platform); + $this->assertContains( + 'ALTER TABLE poor_yorick ALTER id TYPE BIGINT', + $sqlStatements, + true + ); + + $this->assertNotContains( + 'ALTER TABLE poor_yorick ALTER id DROP DEFAULT', + $sqlStatements, + true + ); + } + + protected function createTableAndColumn($schema, $type){ + $table = $schema->createTable("poor_yorick"); + $table->addColumn('id', $type, [ + 'autoincrement' => true, + 'unsigned' => true, + 'notnull' => true, + 'length' => 11, + ]); + } + +}