Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
This was discovered in #2841291: Fix NOT NULL handling in the entity storage and 'primary key' changes when updating the storage definition of an identifier field.
The field schema data doesn't accurately store the schema for identifier fields (ID and Revision ID): Fields that are stored as serial
in certain tables are actually saved as int
in the field schema data for those tables.
Proposed resolution
Move the ::processIdentifierSchema()
calls from process*Table()
to getSharedTableFieldSchema()
.
Comment | File | Size | Author |
---|---|---|---|
#69 | 2928906-69-9.0.x.patch | 15.04 KB | gnunes |
#69 | 2928906-69-8.9.x.patch | 12.2 KB | gnunes |
#65 | 2928906-65-9.0.x.patch | 15.03 KB | gnunes |
#65 | 2928906-65-8.9.x.patch | 12.2 KB | gnunes |
#60 | 2928906-60-9.0.x.patch | 15.04 KB | gnunes |
Comments
Comment #2
tstoecklerHere we go, this is mostly extracted from #2841291: Fix NOT NULL handling in the entity storage and 'primary key' changes when updating the storage definition of an identifier field, but I started looking around and also started cleaning up some of the overridden storage schema handlers. This is work in progress. I also verified that a bunch of the schema-related tests pass, but let's see what the entire testsuite brings...
Comment #4
tstoecklerInteresting, so the changes I did to the node and user storage schema require an update path. Makes sense. I'm including those update paths for now, but not sure whether that's in scope for this issue. In any case not proceeding with any further conversions until we have some agreement on the general direction of this.
This also brings in the tests from #2841291: Fix NOT NULL handling in the entity storage and 'primary key' changes when updating the storage definition of an identifier field that I had incorrectly left out before (and I learned about
git add -N
because of that, neat! ;-))Comment #5
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedAre you sure that needs to be handled in this issue? I don't remember having a problem with this when I was working on #2841291: Fix NOT NULL handling in the entity storage and 'primary key' changes when updating the storage definition of an identifier field.
Comment #6
tstoecklerWell, we don't have to solve the two issues in the IS in one issue. I just thought it made sense this both of them affect the stored field schema so that we will need to have some update path for both of them. It's not necessary for #2841291: Fix NOT NULL handling in the entity storage and 'primary key' changes when updating the storage definition of an identifier field, but it does avoid the necessity of fetching the entity schema e.g. for creating a composite primary key for example for the data table when making an entity type translatable. But if you prefer to tackle the two issues separately, I'm absolutely fine with that; the first one certainly is smaller in scope, in particular because it will not require any changes to the overridden schema handlers.
Comment #7
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedI think it would make the patch(es) a lot easier to review if we handle them separately :)
Edit: for example, while looking at the latest patch from this issue, it was very hard to determine if a change was needed for the first or the second bug.
Comment #8
tstoecklerSure, no problem. Will move the key/index stuff to #2929120: The stored field schema does not contain field-level primary keys, unique keys and indexes then. Am waiting for a green patch here first, though. Re-titling and updating the issue summary in the meantime.
Comment #9
tstoecklerComment #10
tstoecklerOK, here we go. This should be what remains after I extracted #2929120-2: The stored field schema does not contain field-level primary keys, unique keys and indexes. Let's see if the split worked out.
Comment #12
mayurjadhav CreditAttribution: mayurjadhav at Blisstering Solutions commentedComment #13
tstoecklerUpdating this for 8.6 and also attempting to fix the update path tests. Unfortunately I'm not able to run those locally.
Comment #15
tstoecklerOK, so bringing some more fixes over from #2841291: Fix NOT NULL handling in the entity storage and 'primary key' changes when updating the storage definition of an identifier field seems to help.
Comment #17
tstoecklerWe also need to update the revision ID field, of course.
Comment #19
tstoecklerThough shalt be green.
Comment #20
tstoecklerI managed to extract #2938951: The primary key is not correctly re-created when updating the storage definition of an identifier field into its own issue that both this and #2841291: Fix NOT NULL handling in the entity storage and 'primary key' changes when updating the storage definition of an identifier field now depend on. Marking postponed for now.
Comment #21
tstoecklerThis is now also PP-3.
Rebased on top of the latest patch in #2938951: The primary key is not correctly re-created when updating the storage definition of an identifier field. Should be green, at least I ran all the tests touched here locally.
Comment #24
jibranBlocker is in #2938951: The primary key is not correctly re-created when updating the storage definition of an identifier field
Comment #25
claudiu.cristeaFew remarks...
Probably 8.7.0?
The other patch was committed only in 8.7.x, so this should be also on 8.7.x: system_update_8701()
Should we filter out also entity types that are not ContentEntityTypeInterface?
Should we filter out non-numeric id/revision fields?
We have now drupal-8.4.0.bare.standard.php.gz
This is a new test so let's not use deprecated assertion methods.
It should work w/o resetting the cache.
Comment #26
claudiu.cristeaChanged version by mistake.
Comment #27
tstoecklerThanks for the review @claudiu.cristea, those are all great points! Fixed that and also added the now-required
legacy
group to the update tests and updated the update hook to use the newEntityDefinitionUpdateManager::getEntityTypes()
which allows a slight simplification.Comment #28
tstoecklerI was wondering, by the way, whether it would be nicer to have the update path use the
InstalledSchemaRepository
directly instead of callingupdateFieldStorageDefinition()
as that would allow us to get rid of the hack in the runtime check inSqlContentEntityStorageSchema
. Not sure what others think about that...Comment #30
geaseRe-rolled the patch from #27 against 8.8.x
Comment #33
geaseReroll for 8.9.x, basically just changing the hook_updateN() number.
Comment #34
daffie CreditAttribution: daffie commented@gease: I would like to see this issue fixed. Could you fix the test failures. I will review.
Comment #35
hchonovThe problem was that during the update to revisionable the storage wasn't using the new table mapping and entity type and therefore the result of
$storage->getRevisionTable()
wasNULL
instead of the correct one.Comment #37
hchonovThis ensures that we will not be touching deleted entity types, which was the reason for the last 2 fails.
Comment #38
hchonovJust a code stylistic change :).
Comment #39
daffie CreditAttribution: daffie commentedThe patch looks good to me. I only have one worry about it:
I am worried that there could be multiple differences between the
$definition_spec
and$stored_spec
, which with the above code will return as FALSE. I am not sure if it is possible. What do you think @hchonov?Update: What are we exactly testing in this test? I do not understand why we have this test.
Comment #40
hchonov@daffie, very good catch!
I've updated the code now to ensure that we properly compare the complete arrays by setting the type of the one to the other's value. We could unset the keys as well before the comparison, but I don't think that the one or the other approach is much better.
The test you are asking about -
EntityUpdateFilledTest
extends fromEntityUpdateTest
and as such uses its test method\Drupal\Tests\system\Functional\Update\EntityUpdateTest::testSystemUpdate8901()
. The only difference is thatEntityUpdateTest
is testing with a Drupal dump installed at 8.4.0 without any content andEntityUpdateFilledTest
is testing with a Drupal dump installed at 8.0 (I think), but filled with content.---
I am uploading two patches now - one if we decide to commit to 8.8.x and onwards and one if we decide to commit to 8.9.x and onwards. The difference is the update function number only.
Comment #41
daffie CreditAttribution: daffie commentedI am happy with this patch, for me it is RTBC.
Comment #42
alexpottDo we need to change the behaviour here? Just in case some alternate implementations rely on this. Or alternatively why are we emptying the methods and not removing them?
Let's use the new 8.8 update files.
Are we sure we want to lose the exactly here?
Comment #43
daffie CreditAttribution: daffie commentedAccording to @catch this issue can no longer land in 8.8.0, but it can land in 8.9.0. All mentions in the patch to 8.8 need to be updated.
Comment #44
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedI think #28 is the better way to handle this, since the actual database schema is not impacted by this change, we should update only the last installed schema definitions which would allow us to get rid of this hunk.
Since Drupal 8.7, it is possible (and required for update repeatability) to update the schema of deleted entity types as well, so this isn't needed anymore.
This test class is not needed, we're not doing any database schema changes that could impact existing data.
This test class should be renamed to something less generic because it's only testing the update of the last installed schema definitions.
Comment #45
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedComment #46
hchonovRe #42:
1.
We don't really change the behavior. Now the identifiers are processed right before calling the methods
::processBaseTable()
and::processRevisionTable()
. However those are not the only methods for processing tables. We have also::processDataTable()
and::processRevisionDataTable()
. Therefore I think it makes sense to keep them if not for not breaking overrides, but at least for consistency.2.
Done.
3.
I don't really have a strong preference here, therefore I've changed it back to an
exactly N expectation
.-------
Re #44:
1.
Done.
2.
This was still required for the tests to pass with the previous update approach. I didn't know how else to fix those fails. Maybe you have some ideas what is going on when using
::updateFieldStorageDefinition()
?See the test fails in #35 that were fixed with this change in #37. I've actually created a dedicated issue for this problem as it occurred already in two different issues during updates - #3092497: Deleted entity types now break update tests.
I thought that with the new approach that will work, but it doesn't as in the update I am initializing the storage now and for that the live entity type is being used, which is deleted in some tests and therefore they fail. This does not allow us to update their field definitions. And I don't actually understand why do we have to update fields for deleted entity types?
3.
Removed.
4.
Renamed
EntityUpdateTest
toIdentifierFieldSchemaTypeUpdateTest
.---
I've addressed everything but #44.2 as I don't see how this can be solved. @amateescu, if we really have to solve it, could you please elaborate on why this is necessary and maybe provide some hints on how this can be done? Thanks!
Comment #47
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThe storage handler can be instantiated for deleted entity types like this:
I don't think this change is needed anymore.
These variable names should be lower snake case.
Comment #48
hchonovIt is, without it we cannot properly find the revision table name in
\Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::getSharedTableFieldSchema()
and then the test fail with :.
Which also leads to an exception when executing
EntityUpdateAddRevisionTranslationAffectedTest
. Backtrace:I've attached the complete backtrace.
If you think that this should work, then could you please try it yourself? I've tried a lot of things and I just cannot make it work and I still don't understand the reasoning behind the requirement for this.
Comment #49
hchonovoups.. I was on the 8.8.x when created the previous patch against 8.9.x ... Here is the proper patch now.
Comment #50
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedRe #48:
1. I looked at the code and I see now why we need those changes. Let's ensure that we set the new field storage definitions as well though.
2. That's because
\Drupal\Core\Entity\ContentEntityType::getRevisionMetadataKeys()
should useEntityFieldManager::getActiveFieldStorageDefinitions()
instead ofgetBaseFieldDefinitions()
, but I'm fine with not handling/fixing that in this issue.Made a few small adjustments and I think this is ready to go now.
Comment #51
hchonovI've missed that one, thanks!
IIRC that was left out because of the BC layer over there. As soon as it is removed
\Drupal\Core\Entity\ContentEntityType::getRevisionMetadataKeys()
will not be retrieving any fields anymore.Comment #52
daffie CreditAttribution: daffie commentedThe patch looks good to me. For me just one point left:
@alexpott did not want to move this piece of code. See his comment #42.1. Can we remove these hunks from the patch?
Comment #53
hchonovNo, this is the actual fix of the problem.
The
::process*Table
methods are called only from within\Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::getEntitySchema()
, but::getSharedTableFieldSchema()
is called from everywhere where the schema is needed - for example from within\Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::createSharedTableSchema()
where the field schema data is saved and here the::process*Table
methods are not called. This is the reason for the inconsistency now.::getSharedTableFieldSchema()
is therefore the single only place where we can correctly alter the schema for all use cases.I've also responded to @alexpotts's comment in #46:
Comment #54
daffie CreditAttribution: daffie commented@hchonov: Thank you for your explaination.
With the explaination from @hchonov (thanks for that), the patch is for me RTBC.
All points are addresssed. Thanks everybody for working on this issue.
Comment #55
catchGiven these two methods are empty, why not remove them and the calls to them?
Comment #56
hchonov@alexpott asked the same question in #42:
which I've answered in #46:
Comment #57
catchWe don't have empty protected methods anywhere else for consistency though. If we want to leave them in 8.x just in case, we could still remove them from Drupal 9.
Comment #58
hchonovI am fine with this. I would be fine removing them in Drupal 8 as well if you think we're allowed doing so.
Comment #59
catchLet's add a 9.x version of the patch without the empty methods. Will also probably need a re-roll for context with the update.
Comment #60
gnunes CreditAttribution: gnunes at bio.logis Genetic Information Management GmbH commentedI've re-rolled the patch for Drupal 9. The interdiff contains the removed methods and the resolved conflict in system.install
Comment #61
gnunes CreditAttribution: gnunes at bio.logis Genetic Information Management GmbH commentedAlso, it needs to be reviewed, please.
Comment #62
hchonov@gnunes, thank you.
The rerolls look good, therefore I think it is safe to put it back to RTBC.
Comment #63
alexpottWe should document that these are being removed in Drupal 9.0.x - we can't deprecate them because we're still calling them but a comment as to why they are empty would seem to be a good idea. This comment should reference the change record.
I'm not sure that exactly() is testing much and it certainly less meaningful when it is 30! How about changing these to $this->any()
Comment #64
daffie CreditAttribution: daffie commentedAdded a change record.
For the novice: Removed the 2 methods that the patch makes empty. See the added change record and the comment #63 of @alexpott. Also make the change that @alexpott is requesting in comment #63.3
Comment #65
gnunes CreditAttribution: gnunes at bio.logis Genetic Information Management GmbH commentedI've done the changes suggested in comment #63.2 and #63.3.
It needs to be reviewd, please
Comment #67
hchonov#65 has made the change to any() only in the patch for D9, however we see that for some reason it does not work. However here we are actually adapting the test only to the new count. I don't think as well that we should be using an exact number anywhere in unit tests unless an algorithm is being tested, but however it might be worth to change this at once across all unit tests and concentrate here only on the required changes for the issue. I would therefore prefer to create a dedicated issue for this.
Comment #68
alexpott@hchonov so fails in #65 are instructive!
So the only assertion we're making is about how times something is called. That's not much of a test...
But this flimsy-ness is not introduced here. So sure let's file a follow-up about fixing this test to actually test something more than counts of function calls.
Comment #69
gnunes CreditAttribution: gnunes at bio.logis Genetic Information Management GmbH commentedRe-uploaded patch for 8.9 and reverted latest changes for 9.0
Please review
Comment #70
gnunes CreditAttribution: gnunes at bio.logis Genetic Information Management GmbH commentedChanging status to 'Needs review'.
Follow-up issue created #3112404: Fix unit tests in SqlContentEntityStorageSchemaTest to work properly without depending on nr of times a function is called
Comment #71
hchonovAdding this comment to the 8.9.x patch as requested in #2928906-63: The field schema incorrectly stores serial fields as int.2 is the only change that occurred since the last RTBC. Therefore I think that it is safe to put the issue back to RTBC.
Comment #72
alexpottCommitted ff16159 and pushed to 9.0.x. Thanks!
Committed 23bb593 and pushed to 8.9.x. Thanks!
Fixed coding standards on commit.
I made this change on the D8 patch to comply with coding standards but to also keep the removal notice in the correct place.