From e4ca3d2c7f5f9076b228d0762ee48ae2b1090340 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 21 Oct 2019 20:13:21 +0200 Subject: [PATCH] Filter out well-known Symfony tables from DBAL schema migrations --- Dbal/BlacklistSchemaAssetFilter.php | 29 +++++++++ .../Compiler/WellKnownSchemaFilterPass.php | 65 +++++++++++++++++++ DoctrineBundle.php | 2 + Resources/config/dbal.xml | 4 ++ .../AbstractDoctrineExtensionTest.php | 64 ++++++++++++++++++ ...ell_known_schema_filter_default_tables.xml | 24 +++++++ ..._known_schema_filter_overridden_tables.xml | 45 +++++++++++++ ...ell_known_schema_filter_default_tables.yml | 25 +++++++ ..._known_schema_filter_overridden_tables.yml | 38 +++++++++++ 9 files changed, 296 insertions(+) create mode 100644 Dbal/BlacklistSchemaAssetFilter.php create mode 100644 DependencyInjection/Compiler/WellKnownSchemaFilterPass.php create mode 100644 Tests/DependencyInjection/Fixtures/config/xml/well_known_schema_filter_default_tables.xml create mode 100644 Tests/DependencyInjection/Fixtures/config/xml/well_known_schema_filter_overridden_tables.xml create mode 100644 Tests/DependencyInjection/Fixtures/config/yml/well_known_schema_filter_default_tables.yml create mode 100644 Tests/DependencyInjection/Fixtures/config/yml/well_known_schema_filter_overridden_tables.yml diff --git a/Dbal/BlacklistSchemaAssetFilter.php b/Dbal/BlacklistSchemaAssetFilter.php new file mode 100644 index 000000000..493e5fb42 --- /dev/null +++ b/Dbal/BlacklistSchemaAssetFilter.php @@ -0,0 +1,29 @@ +blacklist = $blacklist; + } + + public function __invoke($assetName) : bool + { + if ($assetName instanceof AbstractAsset) { + $assetName = $assetName->getName(); + } + + return ! in_array($assetName, $this->blacklist, true); + } +} diff --git a/DependencyInjection/Compiler/WellKnownSchemaFilterPass.php b/DependencyInjection/Compiler/WellKnownSchemaFilterPass.php new file mode 100644 index 000000000..e83678421 --- /dev/null +++ b/DependencyInjection/Compiler/WellKnownSchemaFilterPass.php @@ -0,0 +1,65 @@ +getDefinitions() as $definition) { + if ($definition->isAbstract() || $definition->isSynthetic()) { + continue; + } + + switch ($definition->getClass()) { + case PdoAdapter::class: + $blacklist[] = $definition->getArguments()[3]['db_table'] ?? 'cache_items'; + break; + + case PdoSessionHandler::class: + $blacklist[] = $definition->getArguments()[1]['db_table'] ?? 'lock_keys'; + break; + + case PdoStore::class: + $blacklist[] = $definition->getArguments()[1]['db_table'] ?? 'sessions'; + break; + + case Connection::class: + $blacklist[] = $definition->getArguments()[0]['table_name'] ?? 'messenger_messages'; + break; + } + } + + if (! $blacklist) { + return; + } + + $definition = $container->getDefinition('doctrine.dbal.well_known_schema_asset_filter'); + $definition->replaceArgument(0, $blacklist); + + foreach (array_keys($container->getParameter('doctrine.connections')) as $name) { + $definition->addTag('doctrine.dbal.schema_filter', ['connection' => $name]); + } + } +} diff --git a/DoctrineBundle.php b/DoctrineBundle.php index e8111b0a6..f19a1e527 100644 --- a/DoctrineBundle.php +++ b/DoctrineBundle.php @@ -5,6 +5,7 @@ use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DbalSchemaFilterPass; use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\EntityListenerPass; use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\ServiceRepositoryCompilerPass; +use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\WellKnownSchemaFilterPass; use Doctrine\Common\Util\ClassUtils; use Doctrine\ORM\EntityManager; use Doctrine\ORM\Proxy\Autoloader; @@ -37,6 +38,7 @@ public function build(ContainerBuilder $container) $container->addCompilerPass(new DoctrineValidationPass('orm')); $container->addCompilerPass(new EntityListenerPass()); $container->addCompilerPass(new ServiceRepositoryCompilerPass()); + $container->addCompilerPass(new WellKnownSchemaFilterPass()); $container->addCompilerPass(new DbalSchemaFilterPass()); } diff --git a/Resources/config/dbal.xml b/Resources/config/dbal.xml index 6755891d7..917d7df53 100644 --- a/Resources/config/dbal.xml +++ b/Resources/config/dbal.xml @@ -75,6 +75,10 @@ + + + + diff --git a/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php b/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php index 42511b5c6..92f910683 100644 --- a/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php +++ b/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php @@ -4,6 +4,7 @@ use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DbalSchemaFilterPass; use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\EntityListenerPass; +use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\WellKnownSchemaFilterPass; use Doctrine\Bundle\DoctrineBundle\DependencyInjection\DoctrineExtension; use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Schema\AbstractAsset; @@ -804,6 +805,7 @@ public function testDbalSchemaFilterNewConfig() $container = $this->getContainer([]); $loader = new DoctrineExtension(); $container->registerExtension($loader); + $container->addCompilerPass(new WellKnownSchemaFilterPass()); $container->addCompilerPass(new DbalSchemaFilterPass()); // ignore table1 table on "default" connection @@ -841,6 +843,68 @@ public function testDbalSchemaFilterNewConfig() $this->assertNull($connConfig = $getConfiguration('connection3')->getSchemaAssetsFilter()); } + public function testWellKnownSchemaFilterDefaultTables() + { + if (! method_exists(Configuration::class, 'setSchemaAssetsFilter')) { + $this->markTestSkipped('Test requires doctrine/dbal 2.9 or higher'); + } + + $container = $this->getContainer([]); + $loader = new DoctrineExtension(); + $container->registerExtension($loader); + $container->addCompilerPass(new WellKnownSchemaFilterPass()); + $container->addCompilerPass(new DbalSchemaFilterPass()); + + $this->loadFromFile($container, 'well_known_schema_filter_default_tables'); + + $this->compileContainer($container); + + $definition = $container->getDefinition('doctrine.dbal.well_known_schema_asset_filter'); + + $this->assertSame([['cache_items', 'lock_keys', 'sessions', 'messenger_messages']], $definition->getArguments()); + $this->assertSame([['connection' => 'connection1'], ['connection' => 'connection2'], ['connection' => 'connection3']], $definition->getTag('doctrine.dbal.schema_filter')); + + $definition = $container->getDefinition('doctrine.dbal.connection1_schema_asset_filter_manager'); + + $this->assertEquals([new Reference('doctrine.dbal.well_known_schema_asset_filter'), new Reference('doctrine.dbal.connection1_regex_schema_filter')], $definition->getArgument(0)); + + $filter = $container->get('well_known_filter'); + + $this->assertFalse($filter('sessions')); + $this->assertFalse($filter('cache_items')); + $this->assertFalse($filter('lock_keys')); + $this->assertFalse($filter('messenger_messages')); + $this->assertTrue($filter('anything_else')); + } + + public function testWellKnownSchemaFilterOverriddenTables() + { + if (! method_exists(Configuration::class, 'setSchemaAssetsFilter')) { + $this->markTestSkipped('Test requires doctrine/dbal 2.9 or higher'); + } + + $container = $this->getContainer([]); + $loader = new DoctrineExtension(); + $container->registerExtension($loader); + $container->addCompilerPass(new WellKnownSchemaFilterPass()); + $container->addCompilerPass(new DbalSchemaFilterPass()); + + $this->loadFromFile($container, 'well_known_schema_filter_overridden_tables'); + + $this->compileContainer($container); + + $filter = $container->get('well_known_filter'); + + $this->assertFalse($filter('app_session')); + $this->assertFalse($filter('app_cache')); + $this->assertFalse($filter('app_locks')); + $this->assertFalse($filter('app_messages')); + $this->assertTrue($filter('sessions')); + $this->assertTrue($filter('cache_items')); + $this->assertTrue($filter('lock_keys')); + $this->assertTrue($filter('messenger_messages')); + } + public function testEntityListenerResolver() { $container = $this->loadContainer('orm_entity_listener_resolver', ['YamlBundle'], new EntityListenerPass()); diff --git a/Tests/DependencyInjection/Fixtures/config/xml/well_known_schema_filter_default_tables.xml b/Tests/DependencyInjection/Fixtures/config/xml/well_known_schema_filter_default_tables.xml new file mode 100644 index 000000000..a63c484d1 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/config/xml/well_known_schema_filter_default_tables.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/DependencyInjection/Fixtures/config/xml/well_known_schema_filter_overridden_tables.xml b/Tests/DependencyInjection/Fixtures/config/xml/well_known_schema_filter_overridden_tables.xml new file mode 100644 index 000000000..99b2d51cd --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/config/xml/well_known_schema_filter_overridden_tables.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + app_cache + + + + + + app_session + + + + + + app_locks + + + + + app_messages + + + + diff --git a/Tests/DependencyInjection/Fixtures/config/yml/well_known_schema_filter_default_tables.yml b/Tests/DependencyInjection/Fixtures/config/yml/well_known_schema_filter_default_tables.yml new file mode 100644 index 000000000..c99dd8d7c --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/config/yml/well_known_schema_filter_default_tables.yml @@ -0,0 +1,25 @@ +doctrine: + dbal: + default_connection: connection1 + connections: + connection1: + schema_filter: ~^(?!t_)~ + connection2: [] + connection3: [] + +services: + well_known_filter: + alias: 'doctrine.dbal.well_known_schema_asset_filter' + public: true + + symfony.cache: + class: 'Symfony\Component\Cache\Adapter\PdoAdapter' + + symfony.session: + class: 'Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler' + + symfony.lock: + class: 'Symfony\Component\Lock\Store\PdoStore' + + symfony.messenger: + class: 'Symfony\Component\Messenger\Transport\Doctrine\Connection' diff --git a/Tests/DependencyInjection/Fixtures/config/yml/well_known_schema_filter_overridden_tables.yml b/Tests/DependencyInjection/Fixtures/config/yml/well_known_schema_filter_overridden_tables.yml new file mode 100644 index 000000000..7058ae8b1 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/config/yml/well_known_schema_filter_overridden_tables.yml @@ -0,0 +1,38 @@ +doctrine: + dbal: + default_connection: connection1 + connections: + connection1: + schema_filter: ~^(?!t_)~ + connection2: [] + connection3: [] + +services: + well_known_filter: + alias: 'doctrine.dbal.well_known_schema_asset_filter' + public: true + + symfony.cache: + class: 'Symfony\Component\Cache\Adapter\PdoAdapter' + arguments: + - ~ + - ~ + - ~ + - db_table: app_cache + + symfony.session: + class: 'Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler' + arguments: + - ~ + - db_table: app_session + + symfony.lock: + class: 'Symfony\Component\Lock\Store\PdoStore' + arguments: + - ~ + - db_table: app_locks + + symfony.messenger: + class: 'Symfony\Component\Messenger\Transport\Doctrine\Connection' + arguments: + - table_name: app_messages