Problem/Motivation
EntityOwnerTrait::ownerBaseFieldDefinitions() is a helper to define an 'owner' field for entity types.
The DB fields created for the owner field have NOT NULL set on the DB column. However, the entity field definition is not set to required.
This means that unless an entity implements custom logic to ensure the field always has a value set (.e.g. Node::preSave()), it is possible to attempt to save an entity with the owner field's value set to NULL, which then causes a database crash.
Steps to reproduce
1. go to the form to create a media entity
2. fill in everything that's required
3. clear the value in the 'Authored by form element
4. press save.
See #3260175: Saving media entity without an owner crashes.
Proposed resolution
Remaining tasks
User interface changes
API changes
Data model changes
Release notes snippet
Comment | File | Size | Author |
---|---|---|---|
#20 | 3260173-6.patch | 533 bytes | andregp |
| |||
#6 | 3260173-6.patch | 533 bytes | andregp |
#6 | with_uid_key_error.png | 186.65 KB | andregp |
#6 | Authoring_reqired.png | 86.13 KB | andregp |
#6 | with_owner_key_error.png | 187.63 KB | andregp |
Comments
Comment #2
joachim CreditAttribution: joachim at Factorial GmbH commentedComment #3
tstoecklerThanks for opening the issue! Interesting, I would have thought that the default value callback that
EntityOwnerTrait
adds a value in every case. Are you doing anything particular to have the entity (attempt to) save without a value or am I just wrong about that?Regardless, however, the underlying issue is valid, I think. A non-required field should not be
NOT NULL
in the DB. The underlying problem is that we make the assumption that every entity key should have aNOT NULL
constraint. That is a problematic assumption for the reason stated here (among others) and is, theoretically, being fixed in #2841291: Fix NOT NULL handling in the entity storage and 'primary key' changes when updating the storage definition of an identifier field. So adding that as a related issue.Since this is a concrete bug report, though, I think it makes sense to discuss this on its own merits. We already have an exception for the label and the revision translation affected keys to "opt" those "out" of the assumption mentioned above, this happens in
\Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::getSharedTableFieldSchema()
. Adding the relevant snippet here, because I think the context is useful:So one thing we could do is just add the owner key to this list of exceptions and that would fix this concrete issue. Since for nodes the owner will in fact never be NULL, as is noted in the issue summary, this would be an issue if there are SQL queries that rely on the owner index (or
node_field__uid__target_id
index to be precise) onnode_field_data
, as those would suffer a performance decrease due to this. I am not sure, though, A) if there are any queries that use that index (from a cursory glance, nothing stuck out at me - at least nothing that would be run on a regular page - but I am a bit out of my depth here to be honest) and B) if there are any what the concrete performance implications on those queries are. I presume the index was not added "explicitly" either and just came "automatically" because the owner field is an entity reference field and that just adds an index automatically, but not sure...Comment #4
joachim CreditAttribution: joachim as a volunteer commented> Interesting, I would have thought that the default value callback that EntityOwnerTrait adds a value in every case. Are you doing anything particular to have the entity (attempt to) save without a value or am I just wrong about that?
I hadn't spotted the default value callback, but AFAICT field default values are applied when the entity is created, not when it's being saved with empty values.
It's really easy to make this crash Drupal in the UI -- I've added the exact steps to reproduce to the IS.
> Since for nodes the owner will in fact never be NULL, as is noted in the issue summary, this would be an issue if there are SQL queries that rely on the owner index
Querying for entities based on owners is a fairly common use case.
I would say the fix needed here is to make the field required.
Comment #5
andregp CreditAttribution: andregp at CI&T commentedI'll work on this
Comment #6
andregp CreditAttribution: andregp at CI&T commentedI've tried adding the owner key to this list of exceptions as mentioned on #3 but neither of the options bellow worked (you can see the errors on the images).
So I changed "Authored by" field to be required.
Comment #7
tstoecklerFair enough @joachim. From the issue summary I had - apparently wrongly - assumed that you had a legitimate use-case for saving an entity with an empty owner. Absolutely fine with making it required since it effectively is required already. Does that mean, though, that we can remove the respective code in
Node::preSave()
?Comment #8
joachim CreditAttribution: joachim as a volunteer commented> Does that mean, though, that we can remove the respective code in Node::preSave()?
Probably, yes, I think?
- If you're using $node_storage->create() then the default value callback has acted.
- If you're saving a node add form, then the default value callback acted when the entity was created to set into the form's initial values
Comment #9
joachim CreditAttribution: joachim as a volunteer commentedThanks for the patch!
This is the right fix, but in the wrong place -- it should be in EntityOwnerTrait.
Comment #10
andregp CreditAttribution: andregp at CI&T commentedOkay, sorry, I'll change it :)
Comment #11
andregp CreditAttribution: andregp at CI&T commentedSo I made a patch with the fix on the right file and made a second patch that also removes the code mentioned on comment #7-#8 in case you want to commit this too.
Comment #13
andregp CreditAttribution: andregp at CI&T commentedNow the patch is failing because of the NodeEditFormTest::checkVariousAuthoredByValues test.
In the code bellow (line 274-285) the test tries to make the "authored by" field empty.
Comment #14
andregp CreditAttribution: andregp at CI&T commented@joachim @tstoeckler What do you believe is the best approach then? Should we change/remove this part of the test or try another solution?
Comment #15
joachim CreditAttribution: joachim as a volunteer commentedIt looks like the intention is that you can clear the field to change the owner of a node from a specific user to anonymous. That's a useful feature.
In which case, we should keep the code in preSave(), and the test.
(And as a follow-up, we should make this behaviour consistent on all user reference fields.)
Comment #16
andregp CreditAttribution: andregp at CI&T commentedSo I changed the preSave() code in the Media entity to keep consistent with the Node entity. Now you can save the media entities with no Author and it will default to Anonymous.
Is there any other user reference fields I am missing?
Comment #17
andregp CreditAttribution: andregp at CI&T commentedComment #18
joachim CreditAttribution: joachim as a volunteer commented@andregp patch #16 looks good, but it belongs over at #3260175: Saving media entity without an owner crashes. (Though it would be nice if it could be handled at the field level, so all entity types benefit from it.)
I think here, patch #6 is good to go. Should it be re-uploaded so it's the most recent patch, to make it clear to commiters?
Comment #19
andregp CreditAttribution: andregp at CI&T commentedOh sorry, I may have got confused.
Comment #20
andregp CreditAttribution: andregp at CI&T commentedRe-upload of patch #6
It seems to be the best approach for this issue since changing the EntityOwnerTrait.php to have Authoring required triggers the NodeEditFormTest::checkVariousAuthoredByValues test, which tests if the entity receives a Anonymous author when its author is omitted.
Comment #22
andregp CreditAttribution: andregp at CI&T commentedThe failed test was
layout_builder\FunctionalJavascript\ContentPreviewToggleTest::testContentPreviewToggle
. Seems unrelated to this issue so sending back to needs review.Comment #25
nod_Not sure the patch is addressing the issue title directly.
Patch prevents drupal from crashing when a media entity doesn't have an author assigned
Comment #26
catchComment #27
catchCommitted/pushed to 10.1.x, cherry-picked back through to 9.4.x, thanks!
Comment #29
catchComment #30
joachim CreditAttribution: joachim as a volunteer commented> Not sure the patch is addressing the issue title directly.
It's not. The patch was uploaded to the WRONG issue. It should have been for #3260175: Saving media entity without an owner crashes.
This issue was MEANT to be fixing the underlying problem so entity classes don't need this fiddly workaround. It was MEANT to be titled 'EntityOwnerTrait does not define the owner field as required, but the DB field is NOT NULL'.
So now the commit log and the issues are all messed up. What now?
Comment #31
joachim CreditAttribution: joachim as a volunteer commentedThe commits need to be reverted --
1. they're for the wrong issue!
2. The change creates a UX discrepancy, because the field should NOT be required. With nodes, you submit the field EMPTY to mean 'anon user'. If the field on Media is required, then that's not possible. The patch on the other issue DOES make that possible
Comment #33
catchThe commit isn't for the wrong issue, it was patch #20 from this issue, which was based on patch #6, which was RTBCed by @joachim in #18 and again by nod_ in #25. However it was committed with the wrong title, and I think the commit on #3260175: Saving media entity without an owner crashes brings things into line with Node, if not for other modules using the trait.
I think there's a potential field-level fix so that node and media don't have to have the code from #3260175: Saving media entity without an owner crashes, but that felt like follow-up material to me, possibly it should be a new issue and we should just close this one?
Comment #34
joachim CreditAttribution: joachim as a volunteer commented> I think the commit on #3260175: Saving media entity without an owner crashes things into line with Node, if not for other modules using the trait.
Yes, that's correct. That issue brings Media in line with Node: a slightly hacky fix in the Entity class. But it's not fixing it for contrib or custom code using the trait, and it's fiddly that this is needed.
> I think there's a potential field-level fix so that node and media don't have to have the code from
Yes, that was my thinking too. And that should probably be a separate issue.
However, this issue is specifically about this:
> The DB fields created for the owner field have NOT NULL set on the DB column. However, the entity field definition is not set to required.
To rephrase: EntityOwnerTrait defines a base field that is NOT set to required. Normally, if a field isn't required, the DB column is *NOT* set to 'NOT NULL', because it's not required, so it's allowed to be NULL.
But the owner field from EntityOwnerTrait gets its DB column set to 'NOT NULL'.
That seems wrong, and I can't figure out how it's happening.
Comment #35
catchThat's #2841291: Fix NOT NULL handling in the entity storage and 'primary key' changes when updating the storage definition of an identifier field
SqlContentEntityStorageSchema::getSharedTableFieldSchema() is where it happens.
Comment #36
joachim CreditAttribution: joachim as a volunteer commentedAh!
In which case, yes, this issue is a duplicate of (or rather a specific case of) #2841291: Fix NOT NULL handling in the entity storage and 'primary key' changes when updating the storage definition of an identifier field.
And #3260175: Saving media entity without an owner crashes needs a follow-up for a more comprehensive fix.