Adding an index to an entity's schema is not detected as a change in onEntityTypeUpdate

Created on 9 October 2018, about 6 years ago
Updated 17 December 2023, 11 months ago

Problem/Motivation

In Drupal Commerce, we wanted to retroactively apply indexes to our entity schema for commonly queried fields. These are string fields and not entity reference fields, which automatically have generated indices due to their field type definition.

The issue is tracked here: #2907367: Add indexes to important fields β†’ .

We added a custom schema handler and added the following override to getSharedTableFieldSchema to add indexes for specific fields.

+  /**
+   * {@inheritdoc}
+   */
+  protected function getSharedTableFieldSchema(FieldStorageDefinitionInterface $storage_definition, $table_name, array $column_mapping) {
+    $schema = parent::getSharedTableFieldSchema($storage_definition, $table_name, $column_mapping);
+
+    $entity_type = $this->entityManager->getDefinition($storage_definition->getTargetEntityTypeId());
+    $field_indexes = $entity_type->get('field_indexes');
+    foreach ($field_indexes as $field_name) {
+      if ($field_name == $storage_definition->getName()) {
+        $this->addSharedTableFieldIndex($storage_definition, $schema);
+      }
+    }
+
+    return $schema;
+  }

This is a more generic form of what FeedStorageSchema, FileStorageSchema, etc have done. The problem, however, is that the indexes do not seem to be added on the update hook.

This was a sample code that berdir pointed me to via the Paragraphs module

	+
+/**
+ * Ensure new field indexes on the product variation entity.
+ */
+function commerce_product_update_8205() {
+  $entity_type_manager = \Drupal::entityTypeManager();
+  $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
+
+  // Get the current product variation entity type definition, ensure the
+  // storage schema class is set.
+  $entity_type = $entity_type_manager->getDefinition('commerce_product_variation')
+    ->setHandlerClass('storage_schema', CommerceContentEntityStorageSchema::class);
+
+  // Regenerate entity type indexes.
+  $definition_update_manager->updateEntityType($entity_type);
+}

However, after many test runs -- the indices were never created.

To resolve this issue, I had to manually fetch the indexes and install them if they did not exist, in an overridden onEntityTypeUpdate method.

	+  /**
+   * {@inheritdoc}
+   */
+  public function onEntityTypeUpdate(EntityTypeInterface $entity_type, EntityTypeInterface $original) {
+    parent::onEntityTypeUpdate($entity_type, $original);
+
+    // @todo open issue against Drupal core
+    // ::onEntityTypeUpdate will only populate new indexes if the entity has
+    // a change on being translatable, revisionable, or a field change.
+    $entity_schema = $this->getEntitySchema($entity_type, TRUE);
+    $schema_table = $entity_type->getDataTable() ?: $entity_type->getBaseTable();
+    $schema_indexes = $entity_schema[$schema_table]['indexes'];
+    foreach ($schema_indexes as $index_name => $index_fields) {
+      if (!$this->database->schema()->indexExists($schema_table, $index_name)) {
+        $this->database->schema()->addIndex($schema_table, $index_name, $index_fields, $entity_schema[$schema_table]);
+      }
+    }
+  }

Proposed resolution

addSharedTableFieldIndex adds an index to the table schema. Respect this as a change in requiresEntityStorageSchemaChanges

Remaining tasks

Find out why this logic ignores new indices on an entity's table schema

  /**
   * {@inheritdoc}
   */
  public function requiresEntityStorageSchemaChanges(EntityTypeInterface $entity_type, EntityTypeInterface $original) {
    return
      $this->hasSharedTableStructureChange($entity_type, $original) ||
      // Detect changes in key or index definitions.
      $this->getEntitySchemaData($entity_type, $this->getEntitySchema($entity_type, TRUE)) != $this->loadEntitySchemaData($original);
  }

User interface changes

N/A

API changes

N/A

Data model changes

Adds database indices as expected.

πŸ› Bug report
Status

Active

Version

11.0 πŸ”₯

Component
EntityΒ  β†’

Last updated about 5 hours ago

Created by

πŸ‡ΊπŸ‡ΈUnited States mglaman WI, USA

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • πŸ‡ΊπŸ‡ΈUnited States mfb San Francisco

    I'm wondering how a contrib field type should go about adding indexes to its fields? I can't use the same overriden onEntityTypeUpdate() method mentioned above, as it's just a field type not an entity.

  • πŸ‡¬πŸ‡·Greece pappis Greece

    I have the same question.
    I found the following on https://drupal.stackexchange.com/questions/221410/how-to-add-a-database-...

    /**
    * Add SQL index on published_date base field
    */
    function MY_MODULE_update_8010(&$sandbox) {
    // Add index
    // ALTER TABLE $table ADD INDEX $name ($column1, $column2..)
    db_query("ALTER TABLE node_field_data ADD INDEX node_field__published_date (published_date)");
    }

    But after examining the drupal schema db shouldn't the query be something like
    ALTER TABLE node__field_fieldname ADD INDEX node__field_fieldname (field_fieldname_value) ?

  • πŸ‡ΊπŸ‡ΈUnited States mfb San Francisco

    @pappis the query will be different depending whether the field data is stored in a shared table or dedicated table

Production build 0.71.5 2024