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.
Comment | File | Size | Author |
---|---|---|---|
#36 | entity-non_rev_field_update-3056539-36.8.7.patch | 19 KB | plach |
#24 | entity-non_rev_field_update-3056539-24.patch | 19.18 KB | plach |
#24 | entity-non_rev_field_update-3056539-24.interdiff.txt | 1.62 KB | plach |
#19 | entity-non_rev_field_update-3056539-19.test.patch | 10.21 KB | plach |
Comments
Comment #2
catch.
Comment #3
catchAlso 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
Comment #4
richgerdesI 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.Comment #5
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThe 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 :(
Comment #6
rakesh.gectcrLooks like this is similar to this #3075774: Consider replacing EntityLastInstalledSchemaRepositoryInterface with EntityFieldManager in taxonomy_post_update_make_taxonomy_term_revisionable
Comment #7
catchComment #8
Wim LeersDo 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 :(
Comment #10
DamienMcKennaTagging as a requirement for Drupal 9.0-beta1.
Comment #11
DamienMcKennaCould this be handled via a documentation improvement and a change notice?
Comment #12
catchAt 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.
Comment #13
DamienMcKennaSo, something like this?
(skipped the test run because it's just a comment change)
Comment #14
plach.
Comment #15
plachAfter 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 :)
Comment #16
plachThis should fix the issue. Working on test coverage now.
Comment #17
plach.
Comment #18
plachThis should be ready for review, let's see whether the bot agrees.
Comment #19
plachAnd here is a test-only patch.
Comment #21
plach.
Comment #22
plachThis provides some docs improvements and should hopefully be ready to go, reviews welcome :)
Comment #23
hchonovJust documentation nit picks, otherwise it looks good.
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.
Comment #24
plachThanks, this should be better.
Comment #25
hchonovMuch better, thank you.
Comment #26
plachUpdated IS
Comment #27
tim.plunkettI 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!
Comment #28
catchOne question, leaving RTBC because these hunks could be skipped on commit easily.
I don't think we need this comment any more?
Same here.
Comment #29
plach@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.
Comment #33
catchCommitted/pushed to 9.0.x, 8.9.x, and 8.8.x, thanks!
Comment #34
xjmReopening to discuss an 8.7.x backport.
Comment #35
catchThis should be backportable. It would help people stuck on <= 8.7.0 who want to update to 8.7, then 8.8.
Comment #36
plachHere's a backport for 8.7.
Comment #37
plachGreen, back to RTBC.
Comment #39
catchCommitted c3ba0d8 and pushed to 8.7.x. Thanks!
Comment #40
xjm