Query::toIterable() should support mixed result queries with scalar mappings #6642

Open
opened 2026-01-22 15:36:19 +01:00 by admin · 15 comments
Owner

Originally created by @kbond on GitHub (Mar 1, 2021).

This was changed in #8467 (v2.8.2) to fix a memory leak. I'm not sure if this is something that should be "fixed".

Originally created by @kbond on GitHub (Mar 1, 2021). This was changed in #8467 (v2.8.2) to fix a memory leak. I'm not sure if this is something that should be "fixed".
Author
Owner

@beberlei commented on GitHub (Mar 1, 2021):

@kbond did ->iterate() support this? toIterable is so new, i would be confused about this happening from new code, did you migrate to toIterable already from iterate?

@beberlei commented on GitHub (Mar 1, 2021): @kbond did `->iterate()` support this? `toIterable` is so new, i would be confused about this happening from new code, did you migrate to `toIterable` already from `iterate`?
Author
Owner

@alex-dev commented on GitHub (Mar 1, 2021):

I'm pretty sure it was supported by ::iterate(). ::toIterable() used to support it before #8467.

@alex-dev commented on GitHub (Mar 1, 2021): I'm pretty sure it was supported by `::iterate()`. `::toIterable()` used to support it before #8467.
Author
Owner

@kbond commented on GitHub (Mar 1, 2021):

I had a workaround for it to work correctly with ->iterate() (an IterableResult decorator to properly normalize the results), see https://github.com/doctrine/orm/issues/2821#issuecomment-739176588 for reference. I noticed in 2.8.0 when I migrated from iterate to toIterable I no longer needed that workaround.

@kbond commented on GitHub (Mar 1, 2021): I had [a workaround](https://github.com/kbond/skunkworks/blob/9d7d9aa47df8d2824cb311b9899135efe9bcc15e/src/Collection/Doctrine/ORM/Batch/IterableResultDecorator.php) for it to work correctly with `->iterate()` (an `IterableResult` decorator to properly normalize the results), see https://github.com/doctrine/orm/issues/2821#issuecomment-739176588 for reference. I [noticed in 2.8.0](https://github.com/kbond/skunkworks/commit/48853b5c0032b2cddab1106c950f11476123b984) when I migrated from `iterate` to `toIterable` I no longer needed that workaround.
Author
Owner

@beberlei commented on GitHub (Mar 1, 2021):

The problem is that toIterable only supported it because it kept the whole result in memory, which is precisely not what the methods primary use case is about.

@beberlei commented on GitHub (Mar 1, 2021): The problem is that `toIterable` only supported it because it kept the whole result in memory, which is precisely not what the methods primary use case is about.
Author
Owner

@alex-dev commented on GitHub (Mar 1, 2021):

How does that even make sense? By nature SQL results are iterative. No matter what hydration (object, array, scalar) is done, the result is first and foremost iterable. Only exception is for joined entities.
So I just don't understand how scalars (which are not even tracked by Doctrine for anything) could cause a problem.

With what has been said. I will allow myself a few assumptions. ArrayHydrator can just return an array, so there is no reason to have any problem with tracking or keeping results in memory. Same goes for ScalarHydrator. SingleScalarHydrator does not apply in an iterable context. So, we are left with SimpleObjectHydrator and ObjectHydrator. Logicaly, I'd assume the latter to be a more general solution that the former. Skimming the code, this all seems true. So we are left with ObjectHydrator.

Reading more code... We see ObjectHydrator is the only one with an actual ::cleanupAfterRowIteration(). So there is no reason to prevent any other Hydrator from working with mixed result sets... Well, only ArrayHydrator would be useful in an iterable mixed result set, but outright denying it is a problem.

ObjectHydrator... Well first and foremost, ::cleanupAfterRowIteration() does not fix keeping the result set in memory by itself. UoW also keeps it in memory once hydrated and require a manual clear to work. Nothing in the concepts of an ObjectHydrator means no mixed results allowed. This limit feels like contrived logic in need of a refactor. It is not the only bug plaguing hydration: #8099, #8280, #8394 and #8442.

TL:DR

  1. There is no reason to disallow mixed results iteration for any sensible hydrator beside ObjectHydrator.
  2. The above limitations seems to be due to contrived code rather than an actual limit on how Doctrine works. Refactoring hydrator internals could be a better solution.
@alex-dev commented on GitHub (Mar 1, 2021): How does that even make sense? By nature SQL results are iterative. No matter what hydration (object, array, scalar) is done, the result is first and foremost iterable. Only exception is for joined entities. So I just don't understand how scalars (which are not even tracked by Doctrine for anything) could cause a problem. With what has been said. I will allow myself a few assumptions. `ArrayHydrator` can just return an array, so there is no reason to have any problem with tracking or keeping results in memory. Same goes for `ScalarHydrator`. `SingleScalarHydrator` does not apply in an iterable context. So, we are left with `SimpleObjectHydrator` and `ObjectHydrator`. Logicaly, I'd assume the latter to be a more general solution that the former. Skimming the code, this all seems true. So we are left with `ObjectHydrator`. Reading more code... We see `ObjectHydrator` is the only one with an actual `::cleanupAfterRowIteration()`. So there is no reason to prevent any other Hydrator from working with mixed result sets... Well, only `ArrayHydrator` would be useful in an iterable mixed result set, but outright denying it is a problem. `ObjectHydrator`... Well first and foremost, `::cleanupAfterRowIteration()` does not fix keeping the result set in memory by itself. UoW also keeps it in memory once hydrated and require a manual clear to work. Nothing in the concepts of an `ObjectHydrator` means no mixed results allowed. This limit feels like contrived logic in need of a refactor. It is not the only bug plaguing hydration: #8099, #8280, #8394 and #8442. TL:DR 1. There is no reason to disallow mixed results iteration for any sensible hydrator beside `ObjectHydrator`. 2. The above limitations seems to be due to contrived code rather than an actual limit on how Doctrine works. Refactoring hydrator internals could be a better solution.
Author
Owner

@beberlei commented on GitHub (Mar 1, 2021):

@alex-dev the hydrator is unfortunately quite confusing code sometimes and does some weird things even in the non join case, just to keep the join case supported. Of course refactoring could be an easier solution, but this is a bug fix to fix the memory leak, mixed results were accidently supported because of this bug. If you find a way to support this that would be great, I don't have time to investigate this at the time.

@beberlei commented on GitHub (Mar 1, 2021): @alex-dev the hydrator is unfortunately quite confusing code sometimes and does some weird things even in the non join case, just to keep the join case supported. Of course refactoring could be an easier solution, but this is a bug fix to fix the memory leak, mixed results were accidently supported because of this bug. If you find a way to support this that would be great, I don't have time to investigate this at the time.
Author
Owner

@beberlei commented on GitHub (Mar 1, 2021):

@alex-dev see https://github.com/doctrine/orm/issues/2821 for context

@beberlei commented on GitHub (Mar 1, 2021): @alex-dev see https://github.com/doctrine/orm/issues/2821 for context
Author
Owner

@alex-dev commented on GitHub (Mar 2, 2021):

I would not mind working on a full on refactor of the hydrator internals. I just want to be sure it will be useful in Doctrine 3.0 and not be rewritten code.

@alex-dev commented on GitHub (Mar 2, 2021): I would not mind working on a full on refactor of the hydrator internals. I just want to be sure it will be useful in Doctrine 3.0 and not be rewritten code.
Author
Owner

@beberlei commented on GitHub (Mar 2, 2021):

No, a full hydrator refactoring is not going to get merged. I meant maybe this is fixable kust for toIterable. @kbond seems to have had a workaround for iterate

@beberlei commented on GitHub (Mar 2, 2021): No, a full hydrator refactoring is not going to get merged. I meant maybe this is fixable kust for toIterable. @kbond seems to have had a workaround for iterate
Author
Owner

@kbond commented on GitHub (Mar 2, 2021):

My workaround basically just normalized the results of ->iterate() - I don't think it will work for ->toIterable() as an exception is thrown when the result is mixed.

I assume ->iterate() had the same memory leak as ->toIterable()? I see according to #8467, it did not.

@kbond commented on GitHub (Mar 2, 2021): My workaround basically just normalized the results of `->iterate()` - I don't think it will work for `->toIterable()` as an exception is thrown when the result is mixed. ~I assume `->iterate()` had the same memory leak as `->toIterable()`?~ I see according to #8467, it did not.
Author
Owner

@alex-dev commented on GitHub (Mar 2, 2021):

If memory serves me well, ::iterate() had consistency problems with mixed result keys and always returned a struct-like array rather than returning the object for single entity result.

Replacing ::toIterable() with a wrapped call to :iterate() fixing ::iterate quirks would be a better approach.

I will investigate deeper the issues I had with ::iterate() than PR an implementation of ::toIterable().

@alex-dev commented on GitHub (Mar 2, 2021): If memory serves me well, `::iterate()` had consistency problems with mixed result keys and always returned a struct-like array rather than returning the object for single entity result. Replacing `::toIterable()` with a wrapped call to `:iterate()` fixing `::iterate` quirks would be a better approach. I will investigate deeper the issues I had with `::iterate()` than PR an implementation of `::toIterable()`.
Author
Owner

@kbond commented on GitHub (Mar 2, 2021):

If memory serves me well, ::iterate() had consistency problems with mixed result keys and always returned a struct-like array rather than returning the object for single entity result.

This is what my workaround fixed.

@kbond commented on GitHub (Mar 2, 2021): > If memory serves me well, ::iterate() had consistency problems with mixed result keys and always returned a struct-like array rather than returning the object for single entity result. This is what my workaround fixed.
Author
Owner

@alex-dev commented on GitHub (Mar 2, 2021):

Yes, but I see potential issues in your code.
I just wanna collect all the issues I had with ::iterate() and be sure your workaround covers them all.

@alex-dev commented on GitHub (Mar 2, 2021): Yes, but I see potential issues in [your code](https://github.com/kbond/skunkworks/blob/main/src/Collection/Doctrine/ORM/Batch/IterableResultDecorator.php). I just wanna collect all the issues I had with `::iterate()` and be sure your workaround covers them all.
Author
Owner

@kbond commented on GitHub (Mar 2, 2021):

Yes, but I see potential issues in your code.

Yes, I'm sure ;)

I will investigate deeper the issues I had with ::iterate() than PR an implementation of ::toIterable().

Assuming the memory leak isn't re-introduced, hopefully that would be acceptable.

@kbond commented on GitHub (Mar 2, 2021): > Yes, but I see potential issues in your code. Yes, I'm sure ;) > I will investigate deeper the issues I had with ::iterate() than PR an implementation of ::toIterable(). Assuming the memory leak isn't re-introduced, hopefully that would be acceptable.
Author
Owner

@alex-dev commented on GitHub (Mar 22, 2021):

AbstractHydrator::hydrateRow is the problem behind ::iterate. To preserve backward compatibility, I can't change it, but leveraging iterate and unwrapping it should be simple.

The PR shall supersedes #8467.

@alex-dev commented on GitHub (Mar 22, 2021): `AbstractHydrator::hydrateRow` is the problem behind `::iterate`. To preserve backward compatibility, I can't change it, but leveraging iterate and unwrapping it should be simple. The PR shall supersedes #8467.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#6642