This is a rare issue and difficult to reproduce, but I can work on some steps to reproduce on a clean install if requested.
Here's the example where I discovered the issue. Unfortunately, it uses some custom entities, so it's hard to replicate, but it might be informative.
Using this entityQuery:
$query = \Drupal::entityQuery('collection_item');
$query->condition('collection', 2);
$group = $query->orConditionGroup();
$group->condition('item.entity:node.type', ['post'], 'IN');
$group->condition('item.entity:persona.type', ['faculty'], 'IN');
$query->condition($group);
$result = $query->execute();
I would sometimes get back collection_items that were not node or persona entities. Turns out that this is because there were some collection_items to other entity types (e.g. media, taxonomy_term) that had the same integer id as a node or persona.
For example, if there were a collection_item with a dynamic entity reference to a media entity with the id 3 and a persona entity with the 'faculty' bundle and the id 3 (or a 'post' node with the id 3), the media entity would be returned, too.
Here's the actual SQL generated by the above entityQuery:
SELECT base_table.id AS id, base_table.id AS base_table_id
FROM collection_item base_table
INNER JOIN collection_item collection_item ON collection_item.id = base_table.id
LEFT JOIN collection_item collection_item_2 ON collection_item_2.id = base_table.id
LEFT OUTER JOIN node node ON node.nid = collection_item_2.item__target_id_int
LEFT JOIN node_field_data node_field_data ON node_field_data.nid = node.nid
LEFT OUTER JOIN persona persona ON persona.pid = collection_item_2.item__target_id_int
LEFT JOIN persona persona_2 ON persona_2.pid = persona.pid
WHERE (collection_item.collection = '2') AND ((node_field_data.type IN ('post')) or (persona_2.type IN ('faculty')));
And here's that query modified to prevent those accidental extra entity types:
SELECT base_table.id AS id, base_table.id AS base_table_id
FROM collection_item base_table
INNER JOIN collection_item collection_item ON collection_item.id = base_table.id
LEFT JOIN collection_item collection_item_2 ON collection_item_2.id = base_table.id
LEFT OUTER JOIN node node ON node.nid = collection_item_2.item__target_id_int AND collection_item_2.item__target_type = 'node'
LEFT JOIN node_field_data node_field_data ON node_field_data.nid = node.nid
LEFT OUTER JOIN persona persona ON persona.pid = collection_item_2.item__target_id_int AND collection_item_2.item__target_type = 'persona'
LEFT JOIN persona persona_2 ON persona_2.pid = persona.pid
WHERE (collection_item.collection = '2') AND ((node_field_data.type IN ('post')) or (persona_2.type IN ('faculty')));
| Comment | File | Size | Author |
|---|---|---|---|
| #24 | entityquery-reference-joins-should-specify-target_type-3120952-24.patch | 1.47 KB | blackiceua |
| #11 | 3120952-11.patch | 10.17 KB | jibran |
Comments
Comment #2
jeffamHere's a small patch that probably isn't the best way to solve this. But it will at least point us in the direction of a proper solution either here or in core.
Comment #3
jibranAdding the condition makes sense to me. Let's add some test cases as well.
There might be a better way to do this.
Comment #4
jibranLet's add some tests as well.
Comment #5
ericchew commentedI ran into this issue on 1.x today.
Tasks and Notes are custom entities.
This query should load all notes that belong to the passed in task. Today, this query returned a note that belonged to a different entity that had the same ID as the task.
I added this to my query to fix it for now, but there should probably be a 1.x patch to address this too. This patch doesn't apply to 1.11 (haven't checked 1.x-dev).
Comment #6
moshe weitzman commentedNow that its been a year and nobody has stepped up for tests, I think it makes sense to merge the bug fix without tests. Otherwise we are keeping bugs around because we are afraid of them coming back!
Comment #7
jibranI'll write some tests and commit this. :)
Comment #8
jibranThe benefit of tests, the last patch only worked for basefields, not for config fields.
That being said, we do need basefield EFQ test in DER.
This is an unfortunate change as we are not calling the parent method anymore.
Comment #9
jibranAlso need 1.x patch.
Comment #10
jibranTL;DR
Before
After
Comment #11
jibranNow with PHPCS fix.
Comment #13
jibran@chx reviewed it on slack. Committed and pushed to 8.x.2.x. Moving it to 8.x-1.x.
Comment #15
moshe weitzman commentedThanks @jibran and @chx
Comment #16
moshe weitzman commentedAny chance we can get a new release soon?
Comment #17
jibranNext tag will be released when 9.2.x is tagged.
Comment #18
jonbob commentedSince this patch uses the Drupal 9 square bracket syntax, it is incompatible with Drupal 8.
Comment #19
moshe weitzman commentedOMG. Happy 20th Drupal anniversary to @JonBob.
Comment #20
jibranFeel free to attach a D8 compatible patch here. I won't be committed but people can still use it. We can drop D8 support in the next release. I'll update the release notes for the latest release.
Comment #21
jibranRE #18: As we have not removed D8 support form the info file. We have only removed test support. I'll add a BC layer for this in the next release so that it can work with both 8 and 9.
After the next release, we'll drop D8 support as we already have dropped D8 testing support.
Moving back to 2.x and NW as we need a BC patch here.
Comment #22
larowlanThanks Jibran, I think the alternative would be to mark the current release unsupported and instead update the composer.json to indicate that the previous release was the last version with D8 support?
Comment #23
jibranUpdating the composer.json would mean a new commit which means a new release so why not add BC and properly drop support. We should also mark the current release unsupported.
Comment #24
blackiceuaD8 compatible patch for the current 2.x version.
Comment #25
attisan#24 works ✨ ... had been a nightmare to find the reason for entity queries being broken when using references.
Comment #27
agger commentedThe patch in #24 indeed works!
This seems to save our a** this time, so thanks!
Comment #28
mastap commentedWe believe brackets should be replaced by backtick ` everywhere for compatibility