Problem/Motivation
SqlContentEntityStorage::save() and delete() wrap their work in startTransaction(). ContentEntityStorageBase::deleteRevision() does not, although it deletes from the revision table, the revision data table and every dedicated field table, and cascades into field type code and hook_entity_revision_delete(). Each statement commits on its own, so an interrupted process leaves the deletion half done.
If the deleted revision is the one the base table points at, the entity becomes permanently unloadable: buildQuery() INNER JOINs the revision table, so load() and loadByProperties() return nothing although the base row is there, and saving throws:
EntityStorageException: Update existing 'paragraph' entity while changing the ID is not supported.
Only direct SQL can repair it.
Steps to reproduce
- Create a revisionable entity with several revisions.
- In a test module, stop the process inside the delete cascade:
function mymodule_entity_revision_delete(EntityInterface $entity) { exit(1); } - Call
$storage->deleteRevision($vid)for a non-default revision. - The revision row is gone, the revision data row remains. Nothing rolled back.
Proposed resolution
Wrap deleteRevision() in a transaction, as save() and delete() already are. This also makes the cascade into composite children atomic.
Remaining tasks
None.
User interface changes
None.
Introduced terminology
None.
API changes
Exceptions thrown while deleting a revision are wrapped in EntityStorageException.
Data model changes
None.
Release notes snippet
None.
Issue fork drupal-3631485
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
alex.bukach commented