Add repair step to fix SQLite autoincrement
Force Doctrine to generate alter table SQL statements for SQLite to make sure the code from OCSqlitePlatform is triggered.
This commit is contained in:
parent
ac8254de6a
commit
f056558b72
|
@ -13,6 +13,7 @@ use OC\Hooks\Emitter;
|
||||||
use OC\Repair\AssetCache;
|
use OC\Repair\AssetCache;
|
||||||
use OC\Repair\CleanTags;
|
use OC\Repair\CleanTags;
|
||||||
use OC\Repair\Collation;
|
use OC\Repair\Collation;
|
||||||
|
use OC\Repair\SqliteAutoincrement;
|
||||||
use OC\Repair\DropOldTables;
|
use OC\Repair\DropOldTables;
|
||||||
use OC\Repair\FillETags;
|
use OC\Repair\FillETags;
|
||||||
use OC\Repair\InnoDB;
|
use OC\Repair\InnoDB;
|
||||||
|
@ -99,6 +100,7 @@ class Repair extends BasicEmitter {
|
||||||
$steps = array(
|
$steps = array(
|
||||||
new InnoDB(),
|
new InnoDB(),
|
||||||
new Collation(\OC::$server->getConfig(), \OC_DB::getConnection()),
|
new Collation(\OC::$server->getConfig(), \OC_DB::getConnection()),
|
||||||
|
new SqliteAutoincrement(\OC_DB::getConnection()),
|
||||||
new SearchLuceneTables(),
|
new SearchLuceneTables(),
|
||||||
new RepairConfig()
|
new RepairConfig()
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015 Vincent Petry <pvince81@owncloud.com>
|
||||||
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
|
* later.
|
||||||
|
* See the COPYING-README file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OC\Repair;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Platforms\SqlitePlatform;
|
||||||
|
use Doctrine\DBAL\Schema\SchemaException;
|
||||||
|
use Doctrine\DBAL\Schema\SchemaDiff;
|
||||||
|
use Doctrine\DBAL\Schema\TableDiff;
|
||||||
|
use Doctrine\DBAL\Schema\ColumnDiff;
|
||||||
|
use OC\Hooks\BasicEmitter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fixes Sqlite autoincrement by forcing the SQLite table schemas to be
|
||||||
|
* altered in order to retrigger SQL schema generation through OCSqlitePlatform.
|
||||||
|
*/
|
||||||
|
class SqliteAutoincrement extends BasicEmitter implements \OC\RepairStep {
|
||||||
|
/**
|
||||||
|
* @var \OC\DB\Connection
|
||||||
|
*/
|
||||||
|
protected $connection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \OC\DB\Connection $connection
|
||||||
|
*/
|
||||||
|
public function __construct($connection) {
|
||||||
|
$this->connection = $connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName() {
|
||||||
|
return 'Repair SQLite autoincrement';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fix mime types
|
||||||
|
*/
|
||||||
|
public function run() {
|
||||||
|
if (!$this->connection->getDatabasePlatform() instanceof SqlitePlatform) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sourceSchema = $this->connection->getSchemaManager()->createSchema();
|
||||||
|
|
||||||
|
$schemaDiff = new SchemaDiff();
|
||||||
|
|
||||||
|
foreach ($sourceSchema->getTables() as $tableSchema) {
|
||||||
|
$primaryKey = $tableSchema->getPrimaryKey();
|
||||||
|
if (!$primaryKey) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$columnNames = $primaryKey->getColumns();
|
||||||
|
|
||||||
|
// add a column diff for every primary key column,
|
||||||
|
// but do not actually change anything, this will
|
||||||
|
// force the generation of SQL statements to alter
|
||||||
|
// those tables, which will then trigger the
|
||||||
|
// specific SQL code from OCSqlitePlatform
|
||||||
|
try {
|
||||||
|
$tableDiff = new TableDiff($tableSchema->getName());
|
||||||
|
$tableDiff->fromTable = $tableSchema;
|
||||||
|
foreach ($columnNames as $columnName) {
|
||||||
|
$columnSchema = $tableSchema->getColumn($columnName);
|
||||||
|
$columnDiff = new ColumnDiff($columnSchema->getName(), $columnSchema);
|
||||||
|
$tableDiff->changedColumns[] = $columnDiff;
|
||||||
|
$schemaDiff->changedTables[] = $tableDiff;
|
||||||
|
}
|
||||||
|
} catch (SchemaException $e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->connection->beginTransaction();
|
||||||
|
foreach ($schemaDiff->toSql($this->connection->getDatabasePlatform()) as $sql) {
|
||||||
|
$this->connection->query($sql);
|
||||||
|
}
|
||||||
|
$this->connection->commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015 Vincent Petry <pvince81@owncloud.com>
|
||||||
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
|
* later.
|
||||||
|
* See the COPYING-README file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Test\Repair;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for fixing the SQLite id recycling
|
||||||
|
*/
|
||||||
|
class TestRepairSqliteAutoincrement extends \Test\TestCase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \OC\Repair\SqliteAutoincrement
|
||||||
|
*/
|
||||||
|
private $repair;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Doctrine\DBAL\Connection
|
||||||
|
*/
|
||||||
|
private $connection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $tableName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \OCP\IConfig
|
||||||
|
*/
|
||||||
|
private $config;
|
||||||
|
|
||||||
|
protected function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->connection = \OC_DB::getConnection();
|
||||||
|
$this->config = \OC::$server->getConfig();
|
||||||
|
if (!$this->connection->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\SqlitePlatform) {
|
||||||
|
$this->markTestSkipped("Test only relevant on Sqlite");
|
||||||
|
}
|
||||||
|
|
||||||
|
$dbPrefix = $this->config->getSystemValue('dbtableprefix', 'oc_');
|
||||||
|
$this->tableName = $this->getUniqueID($dbPrefix . 'autoinc_test');
|
||||||
|
$this->connection->exec('CREATE TABLE ' . $this->tableName . '("someid" INTEGER NOT NULL, "text" VARCHAR(16), PRIMARY KEY("someid"))');
|
||||||
|
|
||||||
|
$this->repair = new \OC\Repair\SqliteAutoincrement($this->connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tearDown() {
|
||||||
|
$this->connection->getSchemaManager()->dropTable($this->tableName);
|
||||||
|
parent::tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether autoincrement works
|
||||||
|
*
|
||||||
|
* @return boolean true if autoincrement works, false otherwise
|
||||||
|
*/
|
||||||
|
protected function checkAutoincrement() {
|
||||||
|
$this->connection->executeUpdate('INSERT INTO ' . $this->tableName . ' ("text") VALUES ("test")');
|
||||||
|
$insertId = $this->connection->lastInsertId();
|
||||||
|
$this->connection->executeUpdate('DELETE FROM ' . $this->tableName . ' WHERE "someid" = ?', array($insertId));
|
||||||
|
|
||||||
|
// insert again
|
||||||
|
$this->connection->executeUpdate('INSERT INTO ' . $this->tableName . ' ("text") VALUES ("test2")');
|
||||||
|
$newInsertId = $this->connection->lastInsertId();
|
||||||
|
|
||||||
|
return ($insertId !== $newInsertId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConvertIdColumn() {
|
||||||
|
$this->assertFalse($this->checkAutoincrement());
|
||||||
|
|
||||||
|
$this->repair->run();
|
||||||
|
|
||||||
|
$this->assertTrue($this->checkAutoincrement());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue