Problem/Motivation

Currently when you have a revisionable entity type with a non revisionable storage definition requiring dedicated table, the table mapping returns no dedicated revision table for it, while the storage and storage schema handlers expect the revision table to be there and try to read from/write to it. Since the table mapping is used to generate the temporary schema, the system ends up missing the field revision table when it starts copying data, as reported by Kate Heinlein in #3052464-47: Cannot update to 8.7.0 because of taxonomy_post_update_make_taxonomy_term_revisionable:

My taxonomy_post_update_make_taxonomy_term_revisionable was failing because I had fields on the terms which I created via hook_entity_field_storage_info / hook_entity_bundle_field_info.

SqlFieldableEntityTypeListenerTrait was then failing because it didn't had a temporary table created to copy the field items:

Drupal\Core\Entity\EntityStorageException: The entity update process failed while processing the entity type taxonomy_term, ID: 5. in Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema->copyData() (line 216 of web/core/lib/Drupal/Core/Entity/Sql/SqlFieldableEntityTypeListenerTrait.php).

Drupal\Core\Database\DatabaseExceptionWrapper: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'tmp_db11dbtaxonomy_term_revision__content_type' doesn't exist: INSERT INTO {tmp_db11dbtaxonomy_term_revision__content_type}

My solution was to make those fields revisionable before the update, and the update itself ran without errors.

Proposed resolution

The root cause of this issue is #3113639: The default table mapping and the SQL storage do not agree on whether non-revisionable bundle fields should get a revision table, however fixing that is not trivial, while to make the update work just "compensating" for the table mapping behavior in the storage schema handler so that also field revision tables are created is enough.

While investigating the issue, it was also suggested that this issue could be caused by running the update without installing the storage definitions. This turned out not to be the case but it is still ok to improve the docs as part fo the fix.

Remaining tasks

None

User interface changes

None

API changes

None

Data model changes

None, field revision tables for fields requiring dedicated storage are now created, as expected by the storage handler.

Release notes snippet

An issue preventing non-revisionable entity types from being successfully updated to revisionable when they define non-revisionable fields requiring dedicated field tables has been resolved.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

catch created an issue. See original summary.

catch’s picture

catch’s picture

Also similar looking with the creation of the temp tables, from #3052464-51: Cannot update to 8.7.0 because of taxonomy_post_update_make_taxonomy_term_revisionable

I am hitting a similar on in 8.7.1, which - I am sure - is because I have an entity change code that redefined 'name' field length in the DB. So, it crashes on field length during the copy to the temp table.

So, I am trying to fix it by updating the field definition and it still crashes with field length.
My code is (possibly wrong but just in case):

  $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
  $last_installed_schema_repository = \Drupal::service('entity.last_installed_schema.repository');

  $entity_type = $definition_update_manager->getEntityType('taxonomy_term');
  $field_storage_definitions = $last_installed_schema_repository->getLastInstalledFieldStorageDefinitions('taxonomy_term');
  $name_definition = $field_storage_definitions['name'];
  $name_definition->setSetting('max_length', 2048);
  $definition_update_manager->updateFieldableEntityType($entity_type, $field_storage_definitions, $sandbox);

BUT this makes me wonder on HOW the temporary tables are created? Is it possible that those temp tables are created from an original rather than last installed schema definition? That would explain different possible crashes, as there might be schema inconsistencies.

richgerdes’s picture

I came across this issue while trying to solve #3061950: SQLContentEntityStorage::saveToDedicatedTables() doesn't correctly use storage configuration. A similar issue was presented, which was the result of SQLContentEntityStorage::saveToDedicatedTables() using the incorrect field definitions while saving field data. The attached issue's patch may offer some assistance here.

amateescu’s picture

The problem here is that the documentation for hook_entity_field_storage_info() doesn't say that the field storage definitions returned by that hook also have to run through the regular field storage life-cycle operations via the field storage definition listener (FieldStorageDefinitionListenerInterface::onFieldStorageDefinitionCreate(), ::onFieldStorageDefinitionUpdate() and ::onFieldStorageDefinitionDelete()). For example, configurable fields are doing it in these methods: FieldStorageConfig::preSaveNew(), ::preSaveUpdated() and ::postDelete().

Without doing that, the field storage definition is not tracked by the last installed schema repository so the new entity definition update API can't handle it.

My guess is that people who implemented this hook in custom code didn't think about these additional details (and rightfully so, since it's not documented anywhere), and that's why we've had such a big number of reports for broken updates to 8.7.0 :(

rakesh.gectcr’s picture

catch’s picture

Issue tags: +Drupal 8 upgrade path
Wim Leers’s picture

My guess is that people who implemented this hook in custom code didn't think about these additional details (and rightfully so, since it's not documented anywhere), and that's why we've had such a big number of reports for broken updates to 8.7.0 :(

Do you have concrete proposals on how to proceed with this?

The only thing I can think of right now is to update the contrib modules to not use the hook in this way, but of course that still won't help custom modules :(

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.9 was released on November 6 and is the final full bugfix release for the Drupal 8.7.x series. Drupal 8.7.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.8.0 on December 4, 2019. (Drupal 8.8.0-beta1 is available for testing.)

Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

DamienMcKenna’s picture

Tagging as a requirement for Drupal 9.0-beta1.

DamienMcKenna’s picture

Could this be handled via a documentation improvement and a change notice?

catch’s picture

At the moment that is the only option we have here, I think it's probably enough to unblock the beta. Would be good to know which contributed modules are affected and open issues against them too.

DamienMcKenna’s picture

Status: Active » Needs review
FileSize
997 bytes

So, something like this?

(skipped the test run because it's just a comment change)

plach’s picture

Issue summary: View changes

.

plach’s picture

Title: SqlFieldableEntityTypeListenerTrait doesn't take additional entity base fields into account » Updating an entity type from non-revisionable to revisionable fails if it has non-revisionable fields
Assigned: Unassigned » plach
Status: Needs review » Needs work

After investigating this, I found out that this is definitely not a documentation issue: it's a straight bug caused by #3113639: The default table mapping and the SQL storage do not agree on whether non-revisionable bundle fields should get a revision table.

Currently when you have a revisionable entity type with a non revisionable storage definition, the table mapping thinks there should be no dedicated revision table, while the storage and storage schema handlers will expect it to be there and try to read from/wrote to it. Since the table mapping is used to generate the temporary schema, the system ends up missing the revision table when it starts copying data.

Moreover I realized that, despite missing docs, it’s very unlikely that someone misses the installation step, because you can no longer load or save an entity if a field table is missing: you get exceptions all over the place. Adding documentation is still a Good Thing, though :)

plach’s picture

Status: Needs work » Needs review
Issue tags: +Needs tests
FileSize
4.13 KB
3.16 KB

This should fix the issue. Working on test coverage now.

plach’s picture

Title: Updating an entity type from non-revisionable to revisionable fails if it has non-revisionable fields » Updating an entity type from non-revisionable to revisionable fails if it has non-revisionable fields stored in dedicated tables

.

plach’s picture

plach’s picture

Status: Needs review » Needs work

The last submitted patch, 19: entity-non_rev_field_update-3056539-19.test.patch, failed testing. View results

plach’s picture

Status: Needs work » Needs review

.

plach’s picture

This provides some docs improvements and should hopefully be ready to go, reviews welcome :)

hchonov’s picture

Just documentation nit picks, otherwise it looks good.

+++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
@@ -615,15 +622,46 @@ protected function postUpdateEntityTypeSchema(EntityTypeInterface $entity_type,
+   * Gets a list of table names for this entity type, field storage and mapping.
...
+   * The table mapping may not return a dedicated revision table, if the field
+   * is not revisionable.

These are indicating only a single field, however multiple field storage definitions are passed. Also the comment is referring to the table mapping only, but should also indicate that this what the current method is "compensating" for.

plach’s picture

hchonov’s picture

Status: Needs review » Reviewed & tested by the community

Much better, thank you.

plach’s picture

Issue summary: View changes

Updated IS

tim.plunkett’s picture

I was reviewing this on Friday and got distracted, and it has since been marked RTBC. Chiming in to say that the patch looks great! Thanks all.
Updated credit field, please do not credit me!

catch’s picture

One question, leaving RTBC because these hunks could be skipped on commit easily.

  1. +++ b/core/lib/Drupal/Core/Entity/entity.api.php
    @@ -1828,6 +1828,11 @@ function hook_entity_form_display_alter(\Drupal\Core\Entity\Display\EntityFormDi
      *
    + * Field (storage) definitions returned by this hook must run through the
    + * regular field storage life-cycle operations: they need to be properly
    + * installed, updated, and uninstalled. This would typically be done through the
    + * Entity Update API provided by the entity definition update manager.
    + *
    

    I don't think we need this comment any more?

  2. +++ b/core/lib/Drupal/Core/Entity/entity.api.php
    @@ -1943,6 +1950,11 @@ function hook_entity_bundle_field_info_alter(&$fields, \Drupal\Core\Entity\Entit
      * Provides field storage definitions for a content entity type.
      *
    + * Field storage definitions returned by this hook must run through the regular
    + * field storage life-cycle operations: they need to be properly installed,
    + * updated, and uninstalled. This would typically be done through the Entity
    + * Update API provided by the entity definition update manager.
    + *
      * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
    

    Same here.

plach’s picture

@catch:

Let's say those docs improvements are not strictly needed to fix this issue, but IMO they don't hurt, since they should have been in place already. We could split them to a separate issue, but I'm not sure it's worth.

  • catch committed 3e7619b on 9.0.x
    Issue #3056539 by plach, DamienMcKenna, catch, hchonov, amateescu, tim....

  • catch committed 658907a on 8.9.x
    Issue #3056539 by plach, DamienMcKenna, catch, hchonov, amateescu, tim....

  • catch committed fef86f6 on 8.8.x
    Issue #3056539 by plach, DamienMcKenna, catch, hchonov, amateescu, tim....
catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed/pushed to 9.0.x, 8.9.x, and 8.8.x, thanks!

xjm’s picture

Version: 8.8.x-dev » 8.7.x-dev
Status: Fixed » Patch (to be ported)

Reopening to discuss an 8.7.x backport.

catch’s picture

This should be backportable. It would help people stuck on <= 8.7.0 who want to update to 8.7, then 8.8.

plach’s picture

Assigned: plach » Unassigned
Status: Patch (to be ported) » Needs review
FileSize
19 KB

Here's a backport for 8.7.

plach’s picture

Status: Needs review » Reviewed & tested by the community

Green, back to RTBC.

  • catch committed c3ba0d8 on 8.7.x
    Issue #3056539 by plach, DamienMcKenna, catch, hchonov, amateescu, tim....
catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed c3ba0d8 and pushed to 8.7.x. Thanks!

xjm’s picture

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.