Fields tables are not updated correctly when setting an existing revision as the new default one

Created on 25 August 2021, about 3 years ago
Updated 22 February 2024, 7 months ago

Problem/Motivation

I encounter a problem with the revision system when I try to set an existing revision as the new default one.

The entity is correctly updated (in node_field_data in this case) but fields tables (node__body for example) are not updated and remains at their previous revision ID.

This problem comes from this code snippet in core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php :

// When updating an existing revision, keep the existing records if the
// field values did not change.
if (!$entity->isNewRevision() && $original && !$this->hasFieldValueChanged($field_definition, $entity, $original)) {
  continue;
}

In this case, the revision is not a new one (as it has been created previously as an unpublished revision) and the field values are not found as changed because $original contains the revision we are saving (and not the previous active revision).

So the condition is considere TRUE and the loop trigger the "continue" action and do not update the field table.

Steps to reproduce

  1. Create a node and publish it to have a revision A
  2. Edit the node fields and create so a new revision B but do not publish it
  3. Try to make the new revision B the default one programmatically by using $node->isDefaultRevision(TRUE) and $node->setPublished()
  4. See that the new revision B is correctly set as the default one, but the fields values are the one of the initial revision A.

Proposed resolution

I think the condition needs to be adapted to considere the case of an existing revision becoming the new default one.
So we have to retrieve the current revision id, compare it to the default revision we are saving and allow an update of fields dedicated tables if the revision is the new default one.

The patch attached check this new condition before to skip fields tables update.

🐛 Bug report
Status

Needs work

Version

11.0 🔥

Component
Entity 

Last updated about 13 hours ago

Created by

🇦🇺Australia Nixou Sydney

Live updates comments and jobs are added and updated live.
  • Needs subsystem maintainer review

    It is used to alert the maintainer(s) of a particular core subsystem that an issue significantly impacts their subsystem, and their signoff is needed (see the governance policy draft for more information). Also, if you use this tag, make sure the issue component is set to the correct subsystem. If an issue significantly impacts more than one subsystem, use needs framework manager review instead.

Sign in to follow issues

Merge Requests

Comments & Activities

Not all content is available!

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

  • 🇺🇸United States smustgrave

    This issue is being reviewed by the kind folks in Slack, #needs-review-queue-initiative. We are working to keep the size of Needs Review queue [2700+ issues] to around 400 (1 month or less), following Review a patch or merge request as a guide.

    Did not test.

    This will need a test case to show the issue.

  • 🇸🇪Sweden TwoD Sweden
  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.2 & pgsql-14.1
    last update 10 months ago
    Custom Commands Failed
  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MySQL 5.7 updated deps
    last update 10 months ago
    Custom Commands Failed
  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.2 & sqlite-3.34
    last update 10 months ago
    Custom Commands Failed
  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MariaDB 10.3.22
    last update 10 months ago
    Custom Commands Failed
  • 🇸🇪Sweden TwoD Sweden

    This patch modifies one of the existing revisioning test classes to include changes to a multi-value field (stored in a dedicated table), which is required for the SQL content entity storage implementation to run the code affected by the described issue.

    The use case presented here aims to cut down on the number of required revisions to be created, especially when no fields are actually modified. I have the need for the same use case as the number of revisions created for minor changes can get excessive, and this would be a simple way to avoid some of those.

    The modified test runs a sequence of changes to an entity, creating a new revision each time, to check that certain types of field modifications across translations are either prevented through validation errors or correctly stored. This is similar to having the Content Moderation module active for the entity as it forces revisions to be created for every modification performed via the entity form, or the separate publish-only form.

    The changes add parameters for preventing the new multi-value field from being modified, and preventing a new revision from being created.
    The createRevision() method is still invoked to copy how the entity form sets up the entity, and if no new revision is desired the test simply calls $entity->setNewRevision(FALSE). This also preserves the setup of $entity->original so field modifications are accurately detectable.

    The test data provider adds two steps which first create a new pending Italian revision with changes to all the fields, and then turns that latest revision into the new default revision without any other changes.
    This triggers the observed behavior in the SqlContentEntityStorage class where it skips writing the latest revision's field values into the main table for the entity, leaving them only in the field's revision table, because it does not detect an actual change.
    This does not happen for the label field or the untranslatable field as those are handled by the shared table method, which does not have the same "optimization".

    Note, I added the new multi-value field to the test entity for simplicity, but if the slight additional overhead is considered too much for other tests using the same entity it could be added in the test class itself.

  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.2 & pgsql-14.1
    last update 10 months ago
    30,646 pass, 5 fail
  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MariaDB 10.3.22
    last update 10 months ago
    30,646 pass, 5 fail
  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MySQL 5.7 updated deps
    last update 10 months ago
    30,645 pass, 7 fail
  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.2 & sqlite-3.34
    last update 10 months ago
    30,640 pass, 7 fail
  • 🇸🇪Sweden TwoD Sweden

    It was easier just to use the new field in the modified test class.

  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.2 & pgsql-14.1
    last update 10 months ago
    Custom Commands Failed
  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.2 & sqlite-3.34
    last update 10 months ago
    Custom Commands Failed
  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MariaDB 10.3.22
    last update 10 months ago
    Custom Commands Failed
  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MySQL 5.7 updated deps
    last update 10 months ago
    Custom Commands Failed
  • 🇸🇪Sweden TwoD Sweden

    Sigh...

  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MariaDB 10.3.22
    last update 10 months ago
    30,663 pass, 1 fail
  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.2 & pgsql-14.1
    last update 10 months ago
    30,663 pass, 1 fail
  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.1 & MySQL 5.7 updated deps
    last update 10 months ago
    30,656 pass, 5 fail
  • Open in Jenkins → Open on Drupal.org →
    Environment: PHP 8.2 & sqlite-3.34
    last update 10 months ago
    30,657 pass, 3 fail
  • Merge request !5554Resolve #3229793 "Field tables" → (Open) created by TwoD
  • Status changed to Needs review 10 months ago
  • 🇸🇪Sweden TwoD Sweden

    It's getting really late so I'm just pushed up a new fix based on what @Nixou did.

    I noticed the query was very similar to what getLatestTranslationAffectedRevisionId() already does, but it also handles translations, which weren't always covered with the fix in #6.

    I also had to wrap the new condition in a try/catch because of what happens in FieldableEntityDefinitionUpdateTest. The entity type is in the process of being updated to say it's revisionable, but the tables don't seem to exist (yet?). Maybe the try/catch is hiding another bug, but I don't have time to investigate that further right now.
    Could use an extra pair of eyes if anyone has the time, so marking NR.

  • 🇺🇸United States smustgrave

    Think this is something that will need sub maintainer to take a look at.

  • Status changed to Needs work 7 months ago
  • The Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

    This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

    Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

Production build 0.71.5 2024