Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed array hydration #9186

Open
wants to merge 4 commits into
base: 2.10.x
Choose a base branch
from
Open

Conversation

LinasRam
Copy link

@LinasRam LinasRam commented Nov 11, 2021

Resetting $this->_identifierMap of base element children each time the new to-many relation is added to base element.

Includes a was-failing-but-now-passing test case.

fixes #9185

prtlinas added 3 commits November 15, 2021 10:37
Resetting `$this->_identifierMap` of base element children each time the new to-many relation is added to base element

fixes doctrine#9185
bhushan
bhushan previously approved these changes Nov 15, 2021
Copy link
Contributor

@bhushan bhushan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@greg0ire please review.. looks good to me

Copy link
Member

@greg0ire greg0ire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 questions:

  1. Why does this fix the bug? I understand what the bug is (great explanation BTW), but I don't understand why it happens.
  2. Is this going to have consequences in terms of performance? I think the hydration is in the hot path of doctrine/orm, and I think resetting an array means loosing data. We have a benchmark to test that kind of thing: https://github.com/doctrine/orm/blob/2.10.x/tests/Doctrine/Performance/Hydration/SimpleQueryArrayHydrationPerformanceBench.php

@LinasRam
Copy link
Author

  1. It's difficult to explain... You could comment out my code change and run my written test with debugger. Pay attention at ArrayHydrator:135 when the phone number mapping is being set. After setting 'Article 1' phone number elements, you will get $this->_identifierMap values (I'm only adding u.p relationship here):
[
    'u.p' => [
        '|1' => [
            '|1234567890' => '0',
            '|9876543210' => '1',
        ],
    ],
]

So far, everything works as expected, but then comes 'Article 2' phone numbers in different order than before. If $this->_identifierMap['u.p']['|1'] values are not reset, algorithm gets array index of 9876543210 number from mapping. Index is 1, but on line 135 |9876543210 value in $this->_identifierMap will be overridden to 0 and mapping will look like this:

[
    'u.p' => [
        '|1' => [
            '|1234567890' => '0',
            '|9876543210' => '0',
        ],
    ],
]

Two array elements with different keys, but same values. That will cause unexpected behavior in updateResultPointer method called on line 154. Then, on line 268 if statement will be valid with $index = 1, but $coll will only have one element as the only the first phone number was set till this point (second phone number will be set on next iteration):

$coll = [
    0 => [
        'phonenumber' => '9876543210',
    ],
];

Because of that, on line 269 $coll[1] is treated as null and this is the line where those null values are being set.
So, resetting $this->_identifierMap of base element children each time the new to-many relation is added to base element solves this problem, because new elements are not getting previous element indexes. It may not be the best solution, but it works. I am not deeply familiar with Doctrine internals.
Also, it may not be any easier to understand the bug after my explanation, but I tried my best.

  1. I have run the benchmark, didn't notice performance difference between current and modified version.

@LinasRam
Copy link
Author

Also, while trying to explain this bug right now, I noticed another problem. If you changed the order of article rows in my test case (order by ids: 1, 2, 1, 2), results would be interesting, too.

@greg0ire
Copy link
Member

Thanks for your explanation! I can't say it helps me because I too am not very familiar with that part of the code, but I will try to get the right people to review this.

@beberlei
Copy link
Member

Does ObjectHydrator have similar code to handle this already? I find it strange this never came up before.

I am confused why its nested, since you dont fetch the object but properties. Can you turn this into a real DQL query as per your comment? I am wondering if the problem is just a wong ResultSetMapping

@LinasRam
Copy link
Author

Yeah, query didn't correspond to what I was trying to accomplish. I updated it, I want nested results.

@LinasRam
Copy link
Author

Is anybody looking at this issue? I don't want it to be forgotten.

@derrabus derrabus added the Bug label Dec 13, 2021
Copy link
Contributor

There hasn't been any activity on this pull request in the past 90 days, so it has been marked as stale and it will be closed automatically if no further activity occurs in the next 7 days.
If you want to continue working on it, please leave a comment.

@github-actions github-actions bot added the Stale label Nov 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Array hydrator returns null values instead of actual elements in one-to-many relationships
5 participants