From a3bdb318e9f552f342de401d0aff24384a80fcd8 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Fri, 29 Sep 2017 11:07:50 +0200 Subject: [PATCH] Fix bigint handling on postgres Signed-off-by: Joas Schilling --- lib/private/DB/ConnectionFactory.php | 4 + lib/private/DB/OCPostgreSqlPlatform.php | 94 +++++++++++++++++++++++ tests/lib/DB/OCPostgreSqlPlatformTest.php | 74 ++++++++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 lib/private/DB/OCPostgreSqlPlatform.php create mode 100644 tests/lib/DB/OCPostgreSqlPlatformTest.php diff --git a/lib/private/DB/ConnectionFactory.php b/lib/private/DB/ConnectionFactory.php index d90d7737d4..8848f6db3b 100644 --- a/lib/private/DB/ConnectionFactory.php +++ b/lib/private/DB/ConnectionFactory.php @@ -137,6 +137,10 @@ class ConnectionFactory { } unset($additionalConnectionParams['host']); break; + + case 'pgsql': + $additionalConnectionParams['platform'] = new OCPostgreSqlPlatform(); + break; case 'sqlite3': $journalMode = $additionalConnectionParams['sqlite.journal_mode']; $additionalConnectionParams['platform'] = new OCSqlitePlatform(); diff --git a/lib/private/DB/OCPostgreSqlPlatform.php b/lib/private/DB/OCPostgreSqlPlatform.php new file mode 100644 index 0000000000..524a62c2be --- /dev/null +++ b/lib/private/DB/OCPostgreSqlPlatform.php @@ -0,0 +1,94 @@ + + * + * @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\Platforms\PostgreSqlPlatform; +use Doctrine\DBAL\Schema\ColumnDiff; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Types\Type; + +class OCPostgreSqlPlatform extends PostgreSqlPlatform { + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff){ + $queries = parent::getAlterTableSQL($diff); + foreach ($queries 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]; + $queries[$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)) { + $queryColumnName = $matches[2]; + $columnDiff = $this->findColumnDiffByName($diff, $queryColumnName); + if ($columnDiff && $this->shouldSkipDropDefault($columnDiff)) { + unset($queries[$index]); + continue; + } + } + } + + return $queries; + } + + /** + * We should NOT drop next sequence value if + * - type was changed from INTEGER to BIGINT + * - column keeps an autoincrement + * - default value is kept NULL + * + * @param ColumnDiff $columnDiff + * @return bool + */ + private function shouldSkipDropDefault(ColumnDiff $columnDiff) { + $column = $columnDiff->column; + $fromColumn = $columnDiff->fromColumn; + return $fromColumn->getType()->getName() === Type::INTEGER + && $column->getType()->getName() === Type::BIGINT + && $fromColumn->getDefault() === null + && $column->getDefault() === null + && $fromColumn->getAutoincrement() + && $column->getAutoincrement(); + } + + /** + * @param TableDiff $diff + * @param string $name + * @return ColumnDiff | false + */ + private function findColumnDiffByName(TableDiff $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 0000000000..56fab621cf --- /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, + ]); + } + +}