Commit a3f5ca8a authored by catch's avatar catch

Issue #2338747 by dawehner, chx: Move {router} out of system.install and create the table lazy

parent 252fee22
......@@ -7,6 +7,8 @@
namespace Drupal\Core\Routing;
use Drupal\Core\Database\Query\Query;
use Drupal\Core\Database\SchemaObjectExistsException;
use Drupal\Core\State\StateInterface;
use Symfony\Component\Routing\RouteCollection;
......@@ -14,6 +16,8 @@
/**
* Dumps Route information to a database table.
*
* @see \Drupal\Core\Routing\RouteProvider
*/
class MatcherDumper implements MatcherDumperInterface {
......@@ -97,7 +101,13 @@ public function dump(array $options = array()) {
try {
// We don't use truncate, because it is not guaranteed to be transaction
// safe.
$this->connection->delete($this->tableName)->execute();
try {
$this->connection->delete($this->tableName)
->execute();
}
catch (\Exception $e) {
$this->ensureTableExists();
}
// Split the routes into chunks to avoid big INSERT queries.
$route_chunks = array_chunk($this->routes->all(), 50, TRUE);
......@@ -162,4 +172,85 @@ public function getRoutes() {
return $this->routes;
}
/**
* Checks if the tree table exists and create it if not.
*
* @return bool
* TRUE if the table was created, FALSE otherwise.
*/
protected function ensureTableExists() {
try {
if (!$this->connection->schema()->tableExists($this->tableName)) {
$this->connection->schema()->createTable($this->tableName, $this->schemaDefinition());
return TRUE;
}
}
catch (SchemaObjectExistsException $e) {
// If another process has already created the config table, attempting to
// recreate it will throw an exception. In this case just catch the
// exception and do nothing.
return TRUE;
}
return FALSE;
}
/**
* Defines the schema for the router table.
*
* @return array
* The schema API definition for the SQL storage table.
*/
protected function schemaDefinition() {
$schema = [
'description' => 'Maps paths to various callbacks (access, page and title)',
'fields' => [
'name' => [
'description' => 'Primary Key: Machine name of this route',
'type' => 'varchar_ascii',
'length' => 255,
'not null' => TRUE,
'default' => '',
],
'path' => [
'description' => 'The path for this URI',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
],
'pattern_outline' => [
'description' => 'The pattern',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
],
'fit' => [
'description' => 'A numeric representation of how specific the path is.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
],
'route' => [
'description' => 'A serialized Route object',
'type' => 'blob',
'size' => 'big',
],
'number_parts' => [
'description' => 'Number of parts in this router path.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'size' => 'small',
],
],
'indexes' => [
'pattern_outline_parts' => ['pattern_outline', 'number_parts'],
],
'primary key' => ['name'],
];
return $schema;
}
}
......@@ -210,7 +210,12 @@ public function preLoadRoutes($names) {
$routes = $cache->data;
}
else {
$result = $this->connection->query('SELECT name, route FROM {' . $this->connection->escapeTable($this->tableName) . '} WHERE name IN ( :names[] )', array(':names[]' => $routes_to_load));
try {
$result = $this->connection->query('SELECT name, route FROM {' . $this->connection->escapeTable($this->tableName) . '} WHERE name IN ( :names[] )', array(':names[]' => $routes_to_load));
}
catch (\Exception $e) {
$result = [];
}
$routes = $result->fetchAllKeyed();
$this->cache->set($cid, $routes, Cache::PERMANENT, ['routes']);
......@@ -336,10 +341,15 @@ protected function getRoutesByPath($path) {
// The >= check on number_parts allows us to match routes with optional
// trailing wildcard parts as long as the pattern matches, since we
// dump the route pattern without those optional parts.
$routes = $this->connection->query("SELECT name, route, fit FROM {" . $this->connection->escapeTable($this->tableName) . "} WHERE pattern_outline IN ( :patterns[] ) AND number_parts >= :count_parts", array(
':patterns[]' => $ancestors, ':count_parts' => count($parts),
))
->fetchAll(\PDO::FETCH_ASSOC);
try {
$routes = $this->connection->query("SELECT name, route, fit FROM {" . $this->connection->escapeTable($this->tableName) . "} WHERE pattern_outline IN ( :patterns[] ) AND number_parts >= :count_parts", array(
':patterns[]' => $ancestors, ':count_parts' => count($parts),
))
->fetchAll(\PDO::FETCH_ASSOC);
}
catch (\Exception $e) {
$routes = [];
}
// We sort by fit and name in PHP to avoid a SQL filesort.
usort($routes, array($this, 'routeProviderRouteCompare'));
......
......@@ -24,7 +24,6 @@ class BreakpointDiscoveryTest extends KernelTestBase {
protected function setUp() {
parent::setUp();
$this->installSchema('system', array('router'));
\Drupal::service('theme_handler')->install(array('breakpoint_theme_test'));
}
......
......@@ -59,7 +59,6 @@ protected function setUp() {
$this->installConfig(array('system', 'filter', 'comment'));
// Comment rendering generates links, so build the router.
$this->installSchema('system', array('router'));
$this->container->get('router.builder')->rebuild();
// Set up a field, so that the entity that'll be referenced bubbles up a
......
......@@ -33,7 +33,6 @@ protected function setUp() {
$this->installEntitySchema('node');
$this->installEntitySchema('comment');
$this->installSchema('comment', ['comment_entity_statistics']);
$this->installSchema('system', ['router']);
$this->installConfig(['comment']);
// The entity.node.canonical route must exist when the RDF hook is called.
......
......@@ -30,7 +30,6 @@ class ConfigInstallTest extends KernelTestBase {
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', ['router']);
// Ensure the global variable being asserted by this test does not exist;
// a previous test executed in this request/process might have set it.
......
......@@ -41,7 +41,7 @@ class EditorImageDialogTest extends EntityUnitTestBase {
protected function setUp() {
parent::setUp();
$this->installEntitySchema('file');
$this->installSchema('system', ['router', 'key_value_expire']);
$this->installSchema('system', ['key_value_expire']);
$this->installSchema('node', array('node_access'));
$this->installSchema('file', array('file_usage'));
$this->installConfig(['node']);
......
......@@ -77,7 +77,6 @@ protected function setUp() {
->save();
// The label formatter rendering generates links, so build the router.
$this->installSchema('system', 'router');
$this->container->get('router.builder')->rebuild();
$this->createEntityReferenceField($this->entityType, $this->bundle, $this->fieldName, 'Field test', $this->entityType, 'default', array(), FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
......
......@@ -29,7 +29,7 @@ class FieldImportDeleteUninstallTest extends FieldUnitTestBase {
protected function setUp() {
parent::setUp();
// Module uninstall requires the router and users_data tables.
// Module uninstall requires users_data tables.
// @see drupal_flush_all_caches()
// @see user_modules_uninstalled()
$this->installSchema('user', array('users_data'));
......
......@@ -52,7 +52,7 @@ protected function setUp() {
$this->installEntitySchema('entity_test');
$this->installEntitySchema('user');
$this->installSchema('system', ['router', 'sequences', 'key_value']);
$this->installSchema('system', ['sequences', 'key_value']);
// Set default storage backend and configure the theme system.
$this->installConfig(array('field', 'system'));
......
......@@ -58,7 +58,6 @@ protected function setUp() {
// Configure the theme system.
$this->installConfig(array('system', 'field'));
$this->installSchema('system', 'router');
\Drupal::service('router.builder')->rebuild();
$this->installEntitySchema('entity_test');
......
......@@ -58,7 +58,6 @@ protected function setUp() {
// Configure the theme system.
$this->installConfig(array('system', 'field'));
$this->installSchema('system', 'router');
\Drupal::service('router.builder')->rebuild();
$this->installEntitySchema('entity_test_rev');
......
......@@ -32,7 +32,6 @@ protected function setUp() {
parent::setUp();
$this->installConfig(['system', 'field']);
$this->installSchema('system', 'router');
\Drupal::service('router.builder')->rebuild();
$this->installEntitySchema('entity_test');
}
......
......@@ -41,7 +41,7 @@ class TextFormatElementFormTest extends KernelTestBase implements FormInterface
protected function setUp() {
parent::setUp();
$this->installEntitySchema('user');
$this->installSchema('system', ['sequences', 'router']);
$this->installSchema('system', ['sequences']);
$this->installConfig(['filter', 'filter_test']);
// Filter tips link to the full-page.
\Drupal::service('router.builder')->rebuild();
......
......@@ -63,7 +63,6 @@ abstract class NormalizerTestBase extends KernelTestBase {
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', array('router'));
$this->installEntitySchema('user');
$this->installEntitySchema('entity_test');
// If the concrete test sub-class installs the Node or Comment modules,
......
......@@ -24,15 +24,6 @@ class HelpEmptyPageTest extends KernelTestBase {
*/
public static $modules = ['system', 'help_test', 'user'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', 'router');
}
/**
* {@inheritdoc}
*/
......
......@@ -41,7 +41,6 @@ protected function setUp() {
$this->installEntitySchema('entity_test');
$this->installEntitySchema('configurable_language');
$this->installSchema('system', 'router');
\Drupal::service('router.builder')->rebuild();
// In order to reflect the changes for a multilingual site in the container
......
......@@ -39,7 +39,6 @@ protected function setUp() {
$this->installEntitySchema('menu_link_content');
$this->installEntitySchema('user');
$this->installSchema('system', ['router']);
// Ensure that the weight of module_link_content is higher than system.
// @see menu_link_content_install()
......
......@@ -33,7 +33,6 @@ protected function setUp() {
parent::setUp();
$this->installEntitySchema('menu_link_content');
$this->installSchema('system', 'router');
}
/**
......
......@@ -27,7 +27,6 @@ class MigrateMenuLinkTest extends MigrateDrupal6TestBase {
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', ['router']);
$this->installEntitySchema('menu_link_content');
$this->executeMigrations(['menu', 'menu_links']);
}
......
......@@ -32,7 +32,6 @@ class MigrateMenuLinkTest extends MigrateDrupal7TestBase {
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', ['router']);
$this->installEntitySchema('menu_link_content');
$this->executeMigration('menu');
\Drupal::service('router.builder')->rebuild();
......
......@@ -32,7 +32,6 @@ protected function setUp() {
parent::setUp();
$this->installEntitySchema('menu_link_content');
$this->installSchema('system', ['router']);
// Ensure that the weight of module_link_content is higher than system.
// @see menu_link_content_install()
......
......@@ -29,7 +29,6 @@ class NodeBodyFieldStorageTest extends KernelTestBase {
protected function setUp() {
parent::setUp();
$this->installSchema('system', 'sequences');
$this->installSchema('system', array('router'));
// Necessary for module uninstall.
$this->installSchema('user', 'users_data');
$this->installEntitySchema('user');
......
......@@ -30,7 +30,6 @@ protected function setUp() {
$this->installEntitySchema('entity_test');
$this->installEntitySchema('entity_test_mul');
$this->installSchema('system', 'router');
\Drupal::service('router.builder')->rebuild();
// Adding german language.
......
......@@ -25,7 +25,6 @@ class RestLinkManagerTest extends KernelTestBase {
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', ['router']);
\Drupal::service('router.builder')->rebuild();
}
......
......@@ -25,7 +25,6 @@ protected function setUp() {
$this->installEntitySchema('entity_test_mulrev');
$this->installEntitySchema('user');
$this->installSchema('system', array('router'));
$this->installConfig(array('field'));
\Drupal::service('router.builder')->rebuild();
\Drupal::moduleHandler()->invoke('rest', 'install');
......
......@@ -35,7 +35,6 @@ class MigrateShortcutSetTest extends MigrateDrupal7TestBase {
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', array('router'));
$this->installEntitySchema('shortcut');
$this->installEntitySchema('menu_link_content');
\Drupal::service('router.builder')->rebuild();
......
......@@ -34,7 +34,6 @@ class MigrateShortcutSetUsersTest extends MigrateDrupal7TestBase {
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', array('router'));
$this->installEntitySchema('shortcut');
$this->installEntitySchema('menu_link_content');
$this->installSchema('shortcut', ['shortcut_set_users']);
......
......@@ -35,7 +35,6 @@ class MigrateShortcutTest extends MigrateDrupal7TestBase {
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', array('router'));
$this->installEntitySchema('shortcut');
$this->installEntitySchema('menu_link_content');
\Drupal::service('router.builder')->rebuild();
......
......@@ -87,7 +87,6 @@ protected function setUp() {
parent::setUp();
$this->installSchema('system', 'sequences');
$this->installEntitySchema('user');
$this->installSchema('system', array('router'));
$this->installEntitySchema('menu_link_content');
$account = User::create([
......
......@@ -51,7 +51,6 @@ class AttachedAssetsTest extends KernelTestBase {
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', array('router'));
$this->container->get('router.builder')->rebuild();
$this->assetResolver = $this->container->get('asset.resolver');
......
......@@ -21,7 +21,6 @@ class PageRenderTest extends KernelTestBase {
*/
function testHookPageAttachmentsExceptions() {
$this->enableModules(['common_test', 'system']);
$this->installSchema('system', 'router');
\Drupal::service('router.builder')->rebuild();
$this->assertPageRenderHookExceptions('common_test', 'hook_page_attachments');
......@@ -32,7 +31,6 @@ function testHookPageAttachmentsExceptions() {
*/
function testHookPageAlter() {
$this->enableModules(['common_test', 'system']);
$this->installSchema('system', 'router');
\Drupal::service('router.builder')->rebuild();
$this->assertPageRenderHookExceptions('common_test', 'hook_page_attachments_alter');
......
......@@ -28,7 +28,6 @@ class RenderElementTypesTest extends KernelTestBase {
protected function setUp() {
parent::setUp();
$this->installConfig(array('system'));
$this->installSchema('system', array('router'));
\Drupal::service('router.builder')->rebuild();
}
......
......@@ -22,14 +22,6 @@ class CurrentThemeConditionTest extends KernelTestBase {
*/
public static $modules = array('system', 'theme_test');
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', array('router'));
}
/**
* Tests the current theme condition.
*/
......
......@@ -42,7 +42,7 @@ class PathElementFormTest extends KernelTestBase implements FormInterface {
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', ['router', 'sequences', 'key_value_expire']);
$this->installSchema('system', ['sequences', 'key_value_expire']);
$this->installEntitySchema('user');
\Drupal::service('router.builder')->rebuild();
/** @var \Drupal\user\RoleInterface $role */
......
......@@ -50,7 +50,7 @@ class EntityAutocompleteElementFormTest extends EntityUnitTestBase implements Fo
protected function setUp() {
parent::setUp();
$this->installSchema('system', ['router', 'key_value_expire']);
$this->installSchema('system', ['key_value_expire']);
\Drupal::service('router.builder')->rebuild();
$this->testUser = User::create(array(
......
......@@ -41,7 +41,6 @@ class EntityBundleFieldTest extends EntityUnitTestBase {
protected function setUp() {
parent::setUp();
$this->installSchema('user', array('users_data'));
$this->installSchema('system', array('router'));
$this->moduleHandler = $this->container->get('module_handler');
$this->database = $this->container->get('database');
}
......
......@@ -29,7 +29,6 @@ class EntitySchemaTest extends EntityUnitTestBase {
protected function setUp() {
parent::setUp();
$this->installSchema('user', array('users_data'));
$this->installSchema('system', array('router'));
$this->database = $this->container->get('database');
}
......
......@@ -29,7 +29,7 @@ class FieldWidgetConstraintValidatorTest extends KernelTestBase {
protected function setUp() {
parent::setUp();
$this->installSchema('system', ['router', 'key_value']);
$this->installSchema('system', ['key_value']);
$this->container->get('router.builder')->rebuild();
$this->installEntitySchema('user');
......
......@@ -102,7 +102,6 @@ function testUriFunctions() {
// Test file_create_url()
// TemporaryStream::getExternalUrl() uses Url::fromRoute(), which needs
// route information to work.
$this->installSchema('system', 'router');
$this->container->get('router.builder')->rebuild();
$this->assertTrue(strpos(file_create_url('temporary://test.txt'), 'system/temporary?file=test.txt'), 'Temporary external URL correctly built.');
$this->assertTrue(strpos(file_create_url('public://test.txt'), Settings::get('file_public_path') . '/test.txt'), 'Public external URL correctly built.');
......
......@@ -31,8 +31,6 @@ class StackKernelIntegrationTest extends KernelTestBase {
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', 'router');
\Drupal::service('router.builder')->rebuild();
}
......
......@@ -27,14 +27,6 @@ class MenuLinkDefaultIntegrationTest extends KernelTestBase {
'menu_test',
);
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', array('router'));
}
/**
* Tests moving a static menu link without a specified menu to the root.
*/
......
......@@ -53,7 +53,7 @@ class MenuLinkTreeTest extends KernelTestBase {
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', array('router'));
\Drupal::service('router.builder')->rebuild();
$this->installEntitySchema('menu_link_content');
$this->linkTree = $this->container->get('menu.link_tree');
......
......@@ -29,7 +29,6 @@ class TableTest extends KernelTestBase {
protected function setUp() {
parent::setUp();
$this->installSchema('system', 'router');
\Drupal::service('router.builder')->rebuild();
}
......
......@@ -42,7 +42,6 @@ class RouteNoneTest extends KernelTestBase {
protected function setUp() {
parent::setUp();
$this->installSchema('system', ['router']);
\Drupal::service('router.builder')->rebuild();
$this->urlGenerator = \Drupal::urlGenerator();
......
......@@ -41,7 +41,6 @@ class RouteProcessorCurrentIntegrationTest extends KernelTestBase {
protected function setUp() {
parent::setUp();
$this->installSchema('system', ['router']);
\Drupal::service('router.builder')->rebuild();
$this->urlGenerator = \Drupal::urlGenerator();
......
......@@ -31,7 +31,6 @@ protected function setUp() {
\Drupal::unsetContainer();
parent::setUp();
$this->installSchema('system', ['router']);
\Drupal::service('router.builder')->rebuild();
}
......
......@@ -30,7 +30,6 @@ class ExceptionHandlingTest extends KernelTestBase {
protected function setUp() {
parent::setUp();
$this->installSchema('system', ['router']);
$this->installEntitySchema('date_format');
\Drupal::service('router.builder')->rebuild();
}
......
......@@ -26,15 +26,6 @@ class UrlIntegrationTest extends KernelTestBase {
*/
public static $modules = array('user', 'router_test', 'system');
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', ['router']);
}
/**
* Ensures that the access() method on \Drupal\Core\Url objects works.
*/
......
......@@ -39,7 +39,6 @@ protected function setUp() {
parent::setUp();
// Install default system configuration.
$this->installConfig(array('system'));
$this->installSchema('system', array('router'));
\Drupal::service('router.builder')->rebuild();
$this->interfaceLanguage = \Drupal::languageManager()->getCurrentLanguage();
......
......@@ -46,7 +46,7 @@ class TwigWhiteListTest extends KernelTestBase {
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', array('router', 'sequences'));
$this->installSchema('system', array('sequences'));
$this->installEntitySchema('node');
$this->installEntitySchema('user');
$this->installEntitySchema('taxonomy_term');
......
......@@ -919,55 +919,6 @@ function system_schema() {
),
);
$schema['router'] = array(
'description' => 'Maps paths to various callbacks (access, page and title)',
'fields' => array(
'name' => array(
'description' => 'Primary Key: Machine name of this route',
'type' => 'varchar_ascii',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
'path' => array(
'description' => 'The path for this URI',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
'pattern_outline' => array(
'description' => 'The pattern',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
'fit' => array(
'description' => 'A numeric representation of how specific the path is.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'route' => array(
'description' => 'A serialized Route object',
'type' => 'blob',
'size' => 'big',
),
'number_parts' => array(
'description' => 'Number of parts in this router path.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'size' => 'small',