ENH Exclude a list of models for checking and repairs (#10746)

This commit is contained in:
Guy Sartorelli 2023-04-04 14:22:00 +12:00 committed by GitHub
parent c1427ff9c4
commit 2c874a1e94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 167 additions and 1 deletions

View File

@ -4,8 +4,10 @@ namespace SilverStripe\ORM\Connect;
use Exception; use Exception;
use SilverStripe\Control\Director; use SilverStripe\Control\Director;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Config\Config; use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injector; use SilverStripe\Core\Injector\Injector;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DB; use SilverStripe\ORM\DB;
use SilverStripe\ORM\FieldType\DBField; use SilverStripe\ORM\FieldType\DBField;
use SilverStripe\ORM\FieldType\DBPrimaryKey; use SilverStripe\ORM\FieldType\DBPrimaryKey;
@ -28,6 +30,16 @@ abstract class DBSchemaManager
*/ */
private static $check_and_repair_on_build = true; private static $check_and_repair_on_build = true;
/**
* For large databases you can declare a list of DataObject classes which will be excluded from
* CHECK TABLE and REPAIR TABLE queries during dev/build. Note that the entire inheritance chain
* for that class will be excluded, including both ancestors and descendants.
*
* Only use this configuration if you know what you are doing and have identified specific models
* as being problematic during your dev/build process.
*/
private static array $exclude_models_from_db_checks = [];
/** /**
* Check if tables should be renamed in a case-sensitive fashion. * Check if tables should be renamed in a case-sensitive fashion.
* Note: This should still work even on case-insensitive databases. * Note: This should still work even on case-insensitive databases.
@ -366,7 +378,7 @@ abstract class DBSchemaManager
if (Config::inst()->get(static::class, 'fix_table_case_on_build')) { if (Config::inst()->get(static::class, 'fix_table_case_on_build')) {
$this->fixTableCase($table); $this->fixTableCase($table);
} }
if (Config::inst()->get(static::class, 'check_and_repair_on_build')) { if ($this->canCheckAndRepairTable($table)) {
$this->checkAndRepairTable($table); $this->checkAndRepairTable($table);
} }
@ -849,6 +861,29 @@ abstract class DBSchemaManager
*/ */
abstract public function checkAndRepairTable($tableName); abstract public function checkAndRepairTable($tableName);
/**
* Determines if we should be checking and repairing tables generally, and whether the passed in table
* is on the ignore list.
*/
private function canCheckAndRepairTable(string $tableName): bool
{
if (!Config::inst()->get(static::class, 'check_and_repair_on_build')) {
return false;
}
// Return false if $tableName belongs to any model in the data hierarchy of any class in the ignore list
$ignoreModels = Config::inst()->get(static::class, 'exclude_models_from_db_checks');
if (!empty($ignoreModels)) {
$modelForTable = ClassInfo::class_name(DataObject::getSchema()->tableClass($tableName));
foreach ($ignoreModels as $ignoreModel) {
if (in_array($modelForTable, ClassInfo::dataClassesFor($ignoreModel))) {
return false;
}
}
}
return true;
}
/** /**
* Ensure the given table has the correct case * Ensure the given table has the correct case

View File

@ -0,0 +1,77 @@
<?php
namespace SilverStripe\ORM\Tests;
use ReflectionMethod;
use SilverStripe\Core\Config\Config;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\ORM\Connect\DBSchemaManager;
use SilverStripe\ORM\Tests\DBSchemaManagerTest\ChildClass;
class DBSchemaManagerTest extends SapphireTest
{
protected $usesDatabase = false;
public function provideCanCheckAndRepairTable()
{
return [
// not ignored, but globally not allowed
[
'tableName' => 'DBSchemaManagerTest_SomeModel',
'checkAndRepairOnBuild' => false,
'expected' => false,
],
// allowed because it's not in the ignore list
[
'tableName' => 'DBSchemaManagerTest_SomeModel',
'checkAndRepairOnBuild' => true,
'expected' => true,
],
// not allowed because it's the base class for an ignored class
[
'tableName' => 'DBSchemaManagerTest_BaseClass',
'checkAndRepairOnBuild' => true,
'expected' => false,
],
// not allowed because it's explicitly in the ignore list
[
'tableName' => 'DBSchemaManagerTest_ChildClass',
'checkAndRepairOnBuild' => true,
'expected' => false,
],
// not allowed because it's a subclass of an ignored class
[
'tableName' => 'DBSchemaManagerTest_GrandChildClass',
'checkAndRepairOnBuild' => true,
'expected' => false,
],
];
}
/**
* @dataProvider provideCanCheckAndRepairTable
*/
public function testCanCheckAndRepairTable(string $tableName, bool $checkAndRepairOnBuild, bool $expected)
{
// set config
Config::modify()->set(DBSchemaManager::class, 'check_and_repair_on_build', $checkAndRepairOnBuild);
Config::modify()->set(DBSchemaManager::class, 'exclude_models_from_db_checks', [ChildClass::class]);
$manager = $this->getConcreteSchemaManager();
$reflectionCanCheck = new ReflectionMethod($manager, 'canCheckAndRepairTable');
$reflectionCanCheck->setAccessible(true);
$result = $reflectionCanCheck->invoke($manager, $tableName);
$this->assertSame($expected, $result);
}
/**
* DBSchemaManager is an abstract class - this gives us an instance of a concrete subclass.
* This allows us to test the original abstract implementations of methods without risking accidentally
* testing overridden methods on something like MySQLSchemaManager
*/
private function getConcreteSchemaManager(): DBSchemaManager
{
return $this->getMockBuilder(DBSchemaManager::class)->getMockForAbstractClass();
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace SilverStripe\ORM\Tests\DBSchemaManagerTest;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
class BaseClass extends DataObject implements TestOnly
{
private static $table_name = 'DBSchemaManagerTest_BaseClass';
private static $db = [
'BaseClassField' => 'Varchar',
];
}

View File

@ -0,0 +1,12 @@
<?php
namespace SilverStripe\ORM\Tests\DBSchemaManagerTest;
class ChildClass extends BaseClass
{
private static $table_name = 'DBSchemaManagerTest_ChildClass';
private static $db = [
'ChildField' => 'Varchar',
];
}

View File

@ -0,0 +1,12 @@
<?php
namespace SilverStripe\ORM\Tests\DBSchemaManagerTest;
class GrandChildClass extends ChildClass
{
private static $table_name = 'DBSchemaManagerTest_GrandChildClass';
private static $db = [
'GrandChildField' => 'Varchar',
];
}

View File

@ -0,0 +1,15 @@
<?php
namespace SilverStripe\ORM\Tests\DBSchemaManagerTest;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
class SomeModel extends DataObject implements TestOnly
{
private static $table_name = 'DBSchemaManagerTest_SomeModel';
private static $db = [
'SomeField' => 'Varchar',
];
}