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
There are two issues when using an EFQ with an Or condition on the top level of the Where condition and a nested And condition as a sub condition of the Or condition: for example:
$query = \Drupal::entityQuery('node');
$and_group = $query->andConditionGroup()
->condition('type', 'a')
->condition('status', 1);
$or_group = $query->orConditionGroup()
->condition('type', 'b')
->condition($and_group);
$query->condition($or_group);
->execute();
Problems are:
Conditions are set on the global sql_query object instead of the current condition container, so the Or condition on the top level is overridden by the nested And condition. Already fixed by #2527064: Nested condition groups in entity queries are broken- Parent conditions are not considered while determining whether to use an INNER or a LEFT join. Currently, an INNER join is used for tables required by AND conditions and a LEFT join is used for tables required by OR conditions. However, once an AND condition is nested inside of a top-level OR condition, it's incorrect to use INNER join - even for AND conditions - because that will incorrectly limit the result set, if the other "branch" of the OR condition is TRUE.
Proposed resolution
- Already fixed by #2527064: Nested condition groups in entity queries are broken.
- Pass down the information about existing Or conditions when working with nested conditions.
Comment | File | Size | Author |
---|---|---|---|
#25 | 2580265-25.patch | 2.07 KB | jcisio |
#21 | 2580265-21.patch | 2.1 KB | FeyP |
#21 | 2580265-21-test-only.patch | 500 bytes | FeyP |
Comments
Comment #2
s_leu CreditAttribution: s_leu at MD Systems GmbH commentedHere's a first patch for this that made my query work.
Comment #4
BerdirComment #5
BerdirComment #6
BerdirSo it looks like the first problem was already fixed in #2527064: Nested condition groups in entity queries are broken but not the second part.
Updating issue title and summary.
The way we now pass down that information about something being inside an OR condition group is very ugly, but it's the same pattern as we already use in there, so it made kind of sense to continue with that.
Comment #8
tstoecklerSo if I understand it correctly the problem is the following: Even though we generally use and INNER join for tables required by AND conditions and a LEFT join for tables required by OR conditions, once we are nested inside of a top-level OR condition it's incorrect to use INNER join - even for AND conditions - because that will incorrectly limit the result set if the other "branch" of the OR comes true. Tagged needs issue summary update, because that could be expanded a bit IMO to clarify things.
I think the patch looks fine to me, in principal. Maybe we can find a better name for the property, I think
hasOrCondition
isn't 100% precise. It's really whether it's contained inside of an or condition, no? Now I'm not proposing we call the thingcontainedInOrCondition
, but maybe we can fight something a littler nicer.In any case we should document that variable on
ConditionBase
or wherever it makes sense (I don't the class graph well enough to say off-hand) and then initialize it toFALSE
, then we can skip theempty()
check....and yes, the "Needs tests" tag is already there. So someone has go down into the rabbit hole that is
EntityQueryTest
. Maybe look at #2527064: Nested condition groups in entity queries are broken for inspiration; I cried less than previously expected when writing that test. And we should run the tests here on all DB backends (drupalci++), to be sure we're not causing any trouble for them.Comment #13
FeyP CreditAttribution: FeyP as a volunteer and at werk21 commentedI ended up using
nestedInOrCondition
.Done. Let me know, if this is better.
This was kind of a quick one, since it was enough to fix the already existing test added in #2527064: Nested condition groups in entity queries are broken so that it actually asserts the correct result set.
I'm not sure, what I would need to do here. Could you elaborate on that one or point me to the relevant documentation? That would be appreciated.
Comment #15
BerdirTo run additional test, you click on "Add test/ retest" and then you can select what you want there. Added a test for PostgreSQL and Sqlite.
Comment #17
jcisio CreditAttribution: jcisio as a volunteer and at Axess Open Web Services for Institut français commentedPatch looks good. The name of that variable makes more sense (even I thought it could also be
$nestedInsideOrCondition
, but in any case better than$nestedOrCondition
). The test is correct to add missing red triangles (4 and 12).Already reviewed in #8 so RTBC.
Comment #18
plachThanks for investigating and fixing this tricky issue!
In general the solution looks sensible to me, I have only an architectural concern:
Adding a public member to the SQL condition does not look great to me: we are introducing a backwards dependency between SQL queries and Entity queries, which makes the relationship (softly) circular. Since apparently the SQL layer does not need this flag at all, we should try to move it into the entity query
Condition
class (and make itprotected
).I think something like this should work:
Since we are changing this line, can we replace
==
occurrences with===
ones?Comment #19
plachI tried a slightly different test and it seems not be working:
I would expect this to behave as the original test, since all items have a color, however the assertion fails. I think this means that we need to propagate somehow the flag value upwards in the condition tree.
Comment #20
jcisio CreditAttribution: jcisio as a volunteer and at Axess Open Web Services for Institut français commented4 and 12 don't have color. It's that that makes the INNER JOIN not work.
Comment #21
FeyP CreditAttribution: FeyP as a volunteer and at werk21 commentedThanks to both of you for your review!
Since I was updating the patch anyway, I changed the name of the property to
$nestedInsideOrCondition
.That's much better indeed. Thanks for coming up with that. It worked for me locally and it came back green as well, so I updated the patch.
Sure, I don't see why it shouldn't work with the strict typing.
Regarding #19, jcisio already pointed out, why the test didn't work, so I think we're still good to go.
Comment #23
plachYep, this looks good now, #20 totally makes sense :)
Minor: could we swap the first two tests so that they match those in the IF branch? This makes it immediately visible that we are dealing with the same condition in both cases. We could even store them in a variable before entering the IF/ELSE code block.
Comment #24
plachI'm happy to move this back to RTBC once #23 is addressed :)
Comment #25
jcisio CreditAttribution: jcisio as a volunteer and at Axess Open Web Services for Institut français commentedOk, swapped so that IF matches ELSE. No interdiff because it's trivial.
Comment #26
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedLooks good to me, back to RTBC.
Comment #27
plachSaving credits
Comment #29
plachCommitted e551206 and pushed to 8.6.x. Thanks!
Comment #30
plachBackported to 8.5.x as 5ed644d.