The Relation module adds a field type "endpoint", which has three columns: entity_type, entity_id and r_index. When querying the entities with this field, I would like to be able to add conditions on these three columns. Core's EntityFieldQuery
supports fieldCondition()
, but when invoking this method three times, the endpoint data table is joined three times, one time for each condition. I want it to be joined once and those three conditions should apply to that one join.
The code is:
$this->fieldCondition('endpoints', 'entity_type', $entity_type, '=', $this->delta_group);
$this->fieldCondition('endpoints', 'entity_id', $entity_id, NULL, $this->delta_group);
$this->fieldCondition('endpoints', 'r_index', $r_index, '=', $this->delta_group);
The query contains:
FROM field_data_endpoints field_data_endpoints0
INNER JOIN field_data_endpoints field_data_endpoints1 ON field_data_endpoints1.entity_type = field_data_endpoints0.entity_type AND field_data_endpoints1.entity_id = field_data_endpoints0.entity_id
INNER JOIN field_data_endpoints field_data_endpoints2 ON field_data_endpoints2.entity_type = field_data_endpoints0.entity_type AND field_data_endpoints2.entity_id = field_data_endpoints0.entity_id
(field_data_endpoints0.endpoints_entity_type = 'node') AND
(field_data_endpoints1.endpoints_entity_id = '127793') AND
(field_data_endpoints1.delta = field_data_endpoints0.delta) AND
(field_data_endpoints2.endpoints_r_index = '0') AND
(field_data_endpoints2.delta = field_data_endpoints0.delta) AND
(field_data_endpoints0.deleted = '0') AND
(field_data_endpoints0.entity_type = 'relation') AND
(field_data_endpoints0.bundle = 'media')
I want just one field_data_endpoints
join with three conditions on endpoints_entity_type
, endpoints_entity_id
and endpoints_r_index
instead of three joins with one condition each.
Comment | File | Size | Author |
---|---|---|---|
#59 | interdiff.txt | 4.37 KB | David_Rothstein |
#59 | multicolumn-1859084-59.patch | 10.79 KB | David_Rothstein |
#30 | multicolumn-1859084-30.patch | 9.85 KB | attiks |
#26 | multicolumn-1859084-26.patch | 10.35 KB | Jorrit |
#21 | multicolumn-1859084-21.patch | 10.36 KB | Jorrit |
Comments
Comment #1
Jorrit CreditAttribution: Jorrit commentedThis patch contains just a test, demonstrating the issue.
Comment #3
Jorrit CreditAttribution: Jorrit commentedEclipse still can't make valid patches ....
Comment #5
Jorrit CreditAttribution: Jorrit commentedFix + patch
Comment #6
mikran CreditAttribution: mikran commentedFixing category, and also does this bug exist in D8 EFQ?
Comment #7
Jorrit CreditAttribution: Jorrit commentedThe code has been changed much compared to Drupal 7. From the looks of it, the bug doesn't occur anymore. I will try to port the test to confirm this. Does this need to be done before a commit to Drupal 7 is considered?
Comment #8
Jorrit CreditAttribution: Jorrit commentedI did not have time to test this in Drupal 8. Is it still possible to have this patch reviewed/committed for Drupal 7?
Comment #9
chx CreditAttribution: chx commented> Use the same alias for fields on the same field for the same delta, for instance for two conditions on different columns of the same field.
That's what delta group is for.
Comment #10
Jorrit CreditAttribution: Jorrit commentedExcept that it doesn't work, that is the point of this issue. I am disappointed that you suggest delta group.
As is demonstrated in the initial post of this issue, adding three conditions on the same field with the same delta group results in three separate joins, whereas a much more performant way would be to add one join with three WHERE conditions on that single joined table.
Comment #11
chx CreditAttribution: chx commentedI am so sorry for not reviewing this more carefully. Now I did. I think it's good to go. I am slightly surprised the existing tests didn't catch this.
As for D8, this is all gone and a new system is in place with clean condition groups.
Comment #12
David_Rothstein CreditAttribution: David_Rothstein commentedI think we need to get the tests into Drupal 8 first. (Unless there are equivalent tests already there? But I didn't see any.)
Looking at the code a bit:
1. Is it safe to remove $key from the table alias? (I.e., is it possible that two different fields on the same table will be queried in a way that happens to use the same delta group, and if so, will that break things?)
2. I don't understand the origin of the is_int() check. Is it documented that 'delta_group' has to be an integer? (All the documentation I see says "arbitrary identifier".)
Minor, but don't use t() in assertion messages. Use format_string() for cases like the first, and just a regular untranslated string for cases like the second.
I think it's a little more standard to use a variable_set() here (or state()->set() in Drupal 8).
Comment #13
Jorrit CreditAttribution: Jorrit commented$key
had previously: making the table aliases unique..The delta field is an integer when using the SQL storage engine and delta_group will always be equated to the delta field, so I assumed annever mind, I see that the value doesn't matter.is_int()
check would work. I can change this to a!is_empty()
if you like.$query
is an object and doesn't need to be persisted likevariable_set()
does. Is it really better to usevariable_set()
? I just need to save the value sotestFieldSqlStorageMultipleConditionsDifferentColumns()
can fetch it later. I can also use a static getter/setter.Now I come to think of it: what I did for delta_group may be the same for language_group. I will look into that and into Drupal 8. In any case, thanks for reviewing! I am glad this issue draws a bit of attention.
Comment #14
Jorrit CreditAttribution: Jorrit commentedI have updated the patch with your comments. I'll change the version again after the QA-bot is done.
Comment #15
chx CreditAttribution: chx commentedDont change the version, EntityFieldQuery is gone completely from D8 and entity_query that replaces it is totally different.
Comment #16
mikran CreditAttribution: mikran commentedI'm very happy with the improvements this patch achieves.
Comment #17
adamwhite CreditAttribution: adamwhite commentedI ran across this issue via #1649398: Performance issues with many relations and the patch in #14 seems to have the performance of my relation-heavy site under control. No problems so far.
Comment #18
burningdog CreditAttribution: burningdog commentedI came here via #1970156: MySQL can only use 61 tables in a join: field_data_endpoints on node_save. The patch from #14 does solve the issue (albeit temporarily - as the number of Relations grow so the number of joins will grow until they exceed MySQL's limit again), but when running all tests on all modules which are part of my site, a number of tests fail. Is that to be expected with a core patch like this?
Comment #19
David_Rothstein CreditAttribution: David_Rothstein commentedThe parts I've looked at look pretty good to me now, but the tests still need to go in Drupal 8 first - again, unless someone can point to where equivalent tests already exist? (It doesn't matter if the API changed; the tests are just testing the functionality and we need the tests to verify that the bug doesn't exist and won't exist again.)
Couple small issues in the D7 patch (I didn't review the whole thing though), mostly code style:
Comment #20
David_Rothstein CreditAttribution: David_Rothstein commented@burningdog, do the same tests fail without this applied, or are there new test failures caused by this patch?
Core tests should in theory always pass but I think it's known that some of them are environment-specific and won't always pass for everyone.
Contrib tests, who knows... it depends entirely on the project.
Comment #21
Jorrit CreditAttribution: Jorrit commentedI have implemented all suggested changes. Regarding the first comment: the $query_tables initializer could perhaps be removed, because it will always be initialized with the following line:
$query_tables =& $select_query->getTables();
Only after that line has executed, the code at
$query_tables[$table_alias]
will be run.I will try to test this with Drupal 8 again. Last time I tried I could not get Drupal 8 to work, but that was a while ago.
Comment #23
Jorrit CreditAttribution: Jorrit commentedIt is not easy to port the tests to Drupal 8, the code is entirely different there. For now I change this issue to Drupal 7 to see if the test bot likes the patch.
Comment #24
Jorrit CreditAttribution: Jorrit commented#21: multicolumn-1859084-21.patch queued for re-testing.
Comment #26
Jorrit CreditAttribution: Jorrit commentedRetry.
Comment #27
attiks CreditAttribution: attiks commentedTHANK YOU, works like a charm
Comment #28
David_Rothstein CreditAttribution: David_Rothstein commentedI still think it should be possible to test this in Drupal 8 too. Assigning to myself to work on that, probably sometime in the next week or two.
If I'm wrong, mea culpa in advance because it would mean that I unnecessarily delayed this patch from getting in :(
Comment #29
chx CreditAttribution: chx commentedThere's absolutely nothing that is relevant in D8 to this patch: the whole subsystem is just gone.
Comment #30
attiks CreditAttribution: attiks commentedRerolled for latest D7
Comment #32
Jorrit CreditAttribution: Jorrit commented30: multicolumn-1859084-30.patch queued for re-testing.
Comment #33
chx CreditAttribution: chx commentedBack to rtbc then
Comment #35
Jorrit CreditAttribution: Jorrit commented30: multicolumn-1859084-30.patch queued for re-testing.
Comment #37
jurcello CreditAttribution: jurcello commented30: multicolumn-1859084-30.patch queued for re-testing.
Comment #38
dcam CreditAttribution: dcam commentedLooks like this is still RTBC.
Comment #40
dcam CreditAttribution: dcam commented30: multicolumn-1859084-30.patch queued for re-testing.
Comment #41
chx CreditAttribution: chx commentedComment #44
dcam CreditAttribution: dcam commentedComment #47
dcam CreditAttribution: dcam commentedComment #48
Fabianx CreditAttribution: Fabianx commentedRTBC+1
Comment #49
mgiffordThe latest code has been marked RTBC for 4 months now. What's needed for this to get in an upcoming release?
@David_Rothstein let me know if it's useful to put this in a production environment & report back.
Comment #52
dcam CreditAttribution: dcam commentedComment #55
dcam CreditAttribution: dcam commentedComment #58
mgiffordComment #59
David_Rothstein CreditAttribution: David_Rothstein commentedI'm still not sure this can't be tested in Drupal 8, but I never got around to working on it and it missed one release because of that (sorry) so let's just get it in for now.
However, this:
We can get away with a global variable, but not with putting this directly in a .test file (and with a non-test-module namespace) :) I didn't notice before that that's where it was.
I cleaned that up in the attached patch and moved it to the field_test module instead. Everything else looked good and seems to work well, so since I only changed the tests I'll commit this if it still passes tests.
It's worth noting (and I will in the commit message) that this patch only fixes the issue when a delta or language group are being used, rather than in the general case. For example, something like this:
still results in an unnecessary inner join with this patch applied. So there might be room to fix this further... However, limiting it to queries with a delta or language group is fine for now (and means the impact is much less in case the patch winds up breaking something, which is good also).
Comment #60
chx CreditAttribution: chx commented> I'm still not sure this can't be tested in Drupal 8,
I am. The whole subsytem is rewritten from the ground up. There's not even a trace of this code in D8. It's done 100% differently. I wrote both codebases, you can believe me.
Comment #61
David_Rothstein CreditAttribution: David_Rothstein commentedCommitted to 7.x - thanks!
I have no doubt that the code is very different in Drupal 8... what I'm not sure of is whether Drupal 8 actually has some kind of similar tests (to ensure that the generated queries don't have an unnecessary number of INNER JOINs)... In any case, I'll mark this "Fixed" for now, but still leave assigned to me to remember to potentially look into that.
Comment #64
David_Rothstein CreditAttribution: David_Rothstein commentedDrupal 7.32 was a security release, so this is now scheduled to be in 7.33.