Browse Source

Merge pull request #10555 from nextcloud/bugfix/10518/only-check-added-items

Only enforce schema limits for supported apps
tags/v16.0.0alpha1
blizzz 5 years ago
parent
commit
68952306aa
No account linked to committer's email address
2 changed files with 134 additions and 28 deletions
  1. 39
    13
      lib/private/DB/MigrationService.php
  2. 95
    15
      tests/lib/DB/MigrationsTest.php

+ 39
- 13
lib/private/DB/MigrationService.php View File



use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Platforms\OraclePlatform;
use Doctrine\DBAL\Platforms\PostgreSqlPlatform; use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\SchemaException; use Doctrine\DBAL\Schema\SchemaException;
use Doctrine\DBAL\Schema\Sequence; use Doctrine\DBAL\Schema\Sequence;
use Doctrine\DBAL\Schema\Table;
use OC\App\InfoParser;
use OC\IntegrityCheck\Helpers\AppLocator; use OC\IntegrityCheck\Helpers\AppLocator;
use OC\Migration\SimpleOutput; use OC\Migration\SimpleOutput;
use OCP\AppFramework\App; use OCP\AppFramework\App;
private $connection; private $connection;
/** @var string */ /** @var string */
private $appName; private $appName;
/** @var bool */
private $checkOracle;


/** /**
* MigrationService constructor. * MigrationService constructor.
if ($appName === 'core') { if ($appName === 'core') {
$this->migrationsPath = \OC::$SERVERROOT . '/core/Migrations'; $this->migrationsPath = \OC::$SERVERROOT . '/core/Migrations';
$this->migrationsNamespace = 'OC\\Core\\Migrations'; $this->migrationsNamespace = 'OC\\Core\\Migrations';
$this->checkOracle = true;
} else { } else {
if (null === $appLocator) { if (null === $appLocator) {
$appLocator = new AppLocator(); $appLocator = new AppLocator();
$namespace = App::buildAppNamespace($appName); $namespace = App::buildAppNamespace($appName);
$this->migrationsPath = "$appPath/lib/Migration"; $this->migrationsPath = "$appPath/lib/Migration";
$this->migrationsNamespace = $namespace . '\\Migration'; $this->migrationsNamespace = $namespace . '\\Migration';

$infoParser = new InfoParser();
$info = $infoParser->parse($appPath . '/appinfo/info.xml');
if (!isset($info['dependencies']['database'])) {
$this->checkOracle = true;
} else {
$this->checkOracle = false;
foreach ($info['dependencies']['database'] as $database) {
if (\is_string($database) && $database === 'oci') {
$this->checkOracle = true;
} else if (\is_array($database) && isset($database['@value']) && $database['@value'] === 'oci') {
$this->checkOracle = true;
}
}
}
} }
} }




if ($toSchema instanceof SchemaWrapper) { if ($toSchema instanceof SchemaWrapper) {
$targetSchema = $toSchema->getWrappedSchema(); $targetSchema = $toSchema->getWrappedSchema();
// TODO re-enable once stable14 is branched of: https://github.com/nextcloud/server/issues/10518
// $this->ensureOracleIdentifierLengthLimit($targetSchema, strlen($this->connection->getPrefix()));
if ($this->checkOracle) {
$sourceSchema = $this->connection->createSchema();
$this->ensureOracleIdentifierLengthLimit($sourceSchema, $targetSchema, strlen($this->connection->getPrefix()));
}
$this->connection->migrateToSchema($targetSchema); $this->connection->migrateToSchema($targetSchema);
$toSchema->performDropTableCalls(); $toSchema->performDropTableCalls();
} }
$this->markAsExecuted($version); $this->markAsExecuted($version);
} }


public function ensureOracleIdentifierLengthLimit(Schema $schema, int $prefixLength) {
$sequences = $schema->getSequences();
public function ensureOracleIdentifierLengthLimit(Schema $sourceSchema, Schema $targetSchema, int $prefixLength) {
$sequences = $targetSchema->getSequences();


foreach ($schema->getTables() as $table) {
if (\strlen($table->getName()) - $prefixLength > 27) {
throw new \InvalidArgumentException('Table name "' . $table->getName() . '" is too long.');
foreach ($targetSchema->getTables() as $table) {
try {
$sourceTable = $sourceSchema->getTable($table->getName());
} catch (SchemaException $e) {
if (\strlen($table->getName()) - $prefixLength > 27) {
throw new \InvalidArgumentException('Table name "' . $table->getName() . '" is too long.');
}
$sourceTable = null;
} }


foreach ($table->getColumns() as $thing) { foreach ($table->getColumns() as $thing) {
if (\strlen($thing->getName()) - $prefixLength > 27) {
if ((!$sourceTable instanceof Table || !$sourceTable->hasColumn($thing->getName())) && \strlen($thing->getName()) - $prefixLength > 27) {
throw new \InvalidArgumentException('Column name "' . $table->getName() . '"."' . $thing->getName() . '" is too long.'); throw new \InvalidArgumentException('Column name "' . $table->getName() . '"."' . $thing->getName() . '" is too long.');
} }
} }


foreach ($table->getIndexes() as $thing) { foreach ($table->getIndexes() as $thing) {
if (\strlen($thing->getName()) - $prefixLength > 27) {
if ((!$sourceTable instanceof Table || !$sourceTable->hasIndex($thing->getName())) && \strlen($thing->getName()) - $prefixLength > 27) {
throw new \InvalidArgumentException('Index name "' . $table->getName() . '"."' . $thing->getName() . '" is too long.'); throw new \InvalidArgumentException('Index name "' . $table->getName() . '"."' . $thing->getName() . '" is too long.');
} }
} }


foreach ($table->getForeignKeys() as $thing) { foreach ($table->getForeignKeys() as $thing) {
if (\strlen($thing->getName()) - $prefixLength > 27) {
if ((!$sourceTable instanceof Table || !$sourceTable->hasForeignKey($thing->getName())) && \strlen($thing->getName()) - $prefixLength > 27) {
throw new \InvalidArgumentException('Foreign key name "' . $table->getName() . '"."' . $thing->getName() . '" is too long.'); throw new \InvalidArgumentException('Foreign key name "' . $table->getName() . '"."' . $thing->getName() . '" is too long.');
} }
} }


$primaryKey = $table->getPrimaryKey(); $primaryKey = $table->getPrimaryKey();
if ($primaryKey instanceof Index) {
if ($primaryKey instanceof Index && (!$sourceTable instanceof Table || !$sourceTable->hasPrimaryKey())) {
$indexName = strtolower($primaryKey->getName()); $indexName = strtolower($primaryKey->getName());
$isUsingDefaultName = $indexName === 'primary'; $isUsingDefaultName = $indexName === 'primary';


} }


foreach ($sequences as $sequence) { foreach ($sequences as $sequence) {
if (\strlen($sequence->getName()) - $prefixLength > 27) {
if (!$sourceSchema->hasSequence($sequence->getName()) && \strlen($sequence->getName()) - $prefixLength > 27) {
throw new \InvalidArgumentException('Sequence name "' . $sequence->getName() . '" is too long.'); throw new \InvalidArgumentException('Sequence name "' . $sequence->getName() . '" is too long.');
} }
} }

+ 95
- 15
tests/lib/DB/MigrationsTest.php View File

use Doctrine\DBAL\Schema\ForeignKeyConstraint; use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\SchemaException;
use Doctrine\DBAL\Schema\Sequence; use Doctrine\DBAL\Schema\Sequence;
use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\Table;
use OC\DB\Connection; use OC\DB\Connection;
->method('migrateToSchema'); ->method('migrateToSchema');


$wrappedSchema = $this->createMock(Schema::class); $wrappedSchema = $this->createMock(Schema::class);
// TODO re-enable once stable14 is branched of: https://github.com/nextcloud/server/issues/10518
/*$wrappedSchema->expects($this->once())
$wrappedSchema->expects($this->once())
->method('getTables') ->method('getTables')
->willReturn([]); ->willReturn([]);
$wrappedSchema->expects($this->once()) $wrappedSchema->expects($this->once())
->method('getSequences') ->method('getSequences')
->willReturn([]);*/
->willReturn([]);


$schemaResult = $this->createMock(SchemaWrapper::class); $schemaResult = $this->createMock(SchemaWrapper::class);
$schemaResult->expects($this->once()) $schemaResult->expects($this->once())
->willReturn(\str_repeat('a', 30)); ->willReturn(\str_repeat('a', 30));


$table = $this->createMock(Table::class); $table = $this->createMock(Table::class);
$table->expects($this->once())
$table->expects($this->atLeastOnce())
->method('getName') ->method('getName')
->willReturn(\str_repeat('a', 30)); ->willReturn(\str_repeat('a', 30));


$sequence = $this->createMock(Sequence::class); $sequence = $this->createMock(Sequence::class);
$sequence->expects($this->once())
$sequence->expects($this->atLeastOnce())
->method('getName') ->method('getName')
->willReturn(\str_repeat('a', 30)); ->willReturn(\str_repeat('a', 30));


->method('getSequences') ->method('getSequences')
->willReturn([$sequence]); ->willReturn([$sequence]);


self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$schema, 3]);
$sourceSchema = $this->createMock(Schema::class);
$sourceSchema->expects($this->any())
->method('getTable')
->willThrowException(new SchemaException());
$sourceSchema->expects($this->any())
->method('hasSequence')
->willReturn(false);

self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$sourceSchema, $schema, 3]);
} }


public function testEnsureOracleIdentifierLengthLimitValidWithPrimaryKey() { public function testEnsureOracleIdentifierLengthLimitValidWithPrimaryKey() {
->method('getSequences') ->method('getSequences')
->willReturn([]); ->willReturn([]);


self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$schema, 3]);
$sourceSchema = $this->createMock(Schema::class);
$sourceSchema->expects($this->any())
->method('getTable')
->willThrowException(new SchemaException());
$sourceSchema->expects($this->any())
->method('hasSequence')
->willReturn(false);

self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$sourceSchema, $schema, 3]);
} }


public function testEnsureOracleIdentifierLengthLimitValidWithPrimaryKeyDefault() { public function testEnsureOracleIdentifierLengthLimitValidWithPrimaryKeyDefault() {
->method('getSequences') ->method('getSequences')
->willReturn([]); ->willReturn([]);


self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$schema, 3]);
$sourceSchema = $this->createMock(Schema::class);
$sourceSchema->expects($this->any())
->method('getTable')
->willThrowException(new SchemaException());
$sourceSchema->expects($this->any())
->method('hasSequence')
->willReturn(false);

self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$sourceSchema, $schema, 3]);
} }


/** /**
->method('getTables') ->method('getTables')
->willReturn([$table]); ->willReturn([$table]);


self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$schema, 3]);
$sourceSchema = $this->createMock(Schema::class);
$sourceSchema->expects($this->any())
->method('getTable')
->willThrowException(new SchemaException());
$sourceSchema->expects($this->any())
->method('hasSequence')
->willReturn(false);

self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$sourceSchema, $schema, 3]);
} }


/** /**
->method('getTables') ->method('getTables')
->willReturn([$table]); ->willReturn([$table]);


self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$schema, 3]);
$sourceSchema = $this->createMock(Schema::class);
$sourceSchema->expects($this->any())
->method('getTable')
->willThrowException(new SchemaException());
$sourceSchema->expects($this->any())
->method('hasSequence')
->willReturn(false);

self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$sourceSchema, $schema, 3]);
} }


/** /**
->method('getTables') ->method('getTables')
->willReturn([$table]); ->willReturn([$table]);


self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$schema, 3]);
$sourceSchema = $this->createMock(Schema::class);
$sourceSchema->expects($this->any())
->method('getTable')
->willThrowException(new SchemaException());
$sourceSchema->expects($this->any())
->method('hasSequence')
->willReturn(false);

self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$sourceSchema, $schema, 3]);
} }


/** /**
->method('getTables') ->method('getTables')
->willReturn([$table]); ->willReturn([$table]);


self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$schema, 3]);
$sourceSchema = $this->createMock(Schema::class);
$sourceSchema->expects($this->any())
->method('getTable')
->willThrowException(new SchemaException());
$sourceSchema->expects($this->any())
->method('hasSequence')
->willReturn(false);

self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$sourceSchema, $schema, 3]);
} }


/** /**
->method('getTables') ->method('getTables')
->willReturn([$table]); ->willReturn([$table]);


self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$schema, 3]);
$sourceSchema = $this->createMock(Schema::class);
$sourceSchema->expects($this->any())
->method('getTable')
->willThrowException(new SchemaException());
$sourceSchema->expects($this->any())
->method('hasSequence')
->willReturn(false);

self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$sourceSchema, $schema, 3]);
} }


/** /**
->method('getTables') ->method('getTables')
->willReturn([$table]); ->willReturn([$table]);


self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$schema, 3]);
$sourceSchema = $this->createMock(Schema::class);
$sourceSchema->expects($this->any())
->method('getTable')
->willThrowException(new SchemaException());
$sourceSchema->expects($this->any())
->method('hasSequence')
->willReturn(false);

self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$sourceSchema, $schema, 3]);
} }


/** /**
->method('getSequences') ->method('getSequences')
->willReturn([$sequence]); ->willReturn([$sequence]);


self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$schema, 3]);
$sourceSchema = $this->createMock(Schema::class);
$sourceSchema->expects($this->any())
->method('getTable')
->willThrowException(new SchemaException());
$sourceSchema->expects($this->any())
->method('hasSequence')
->willReturn(false);

self::invokePrivate($this->migrationService, 'ensureOracleIdentifierLengthLimit', [$sourceSchema, $schema, 3]);
} }
} }

Loading…
Cancel
Save