ResultSetMapping will fail silently and return unexpected result set (all null) #5564

Closed
opened 2026-01-22 15:11:23 +01:00 by admin · 5 comments
Owner

Originally created by @JorgeSivil on GitHub (Jun 1, 2017).

Originally assigned to: @Ocramius on GitHub.

I spent 2 hours debugging why this was returning me 139 results all null.

It was that a column had one uppercase char in the name and it was different from the value passed to addFieldResult.

This should throw an exception! This is really very hard to debug.

$rsm->addFieldResult('umpp', 'ID_menu', 'id'); was $rsm->addFieldResult('umpp', 'ID_Menu', 'id');

And then the unexpected result, which is partly correct, because there were 139 rows, but all null.

This is the complete code (I know the table and SQL is ugly, I just copy pasted what client gave me)

public function getEntriesForUserId($userId): array {
        $em = $this->getEntityManager();
        $rsm = new ResultSetMapping();
        $rsm->addEntityResult('AppBundle\Entity\LegacyMenuEntry', 'umpp');
        $rsm->addFieldResult('umpp', 'ID_menu', 'id');
        $rsm->addFieldResult('umpp', 'Nombre', 'nombre');
        $rsm->addFieldResult('umpp', 'Path_logico', 'path_logico');
        $rsm->addFieldResult('umpp', 'Path_real', 'path_real');
        $rsm->addFieldResult('umpp', 'Frame_destino', 'frame_destino');
        $rsm->addFieldResult('umpp', 'Img_cerrado', 'img_cerrado');
        $rsm->addFieldResult('umpp', 'Img_abierto', 'img_abierto');
        $rsm->addFieldResult('umpp', 'z_index', 'z_index');

        $sql = <<<MYSQL
        SELECT
            umpp.*
        FROM
            general.Usuarios_legajo_profile
            JOIN general.Usuarios_menu_profile 
            ON general.Usuarios_legajo_profile.ID_profile = general.Usuarios_menu_profile.ID_profile 
            AND Usuarios_legajo_profile.Usuario = :user_id
            INNER JOIN general.Usuarios_menu_path umpp ON general.Usuarios_menu_profile.ID_menu = umpp.ID_menu
        ORDER BY
            general.Usuarios_legajo_profile.z_index ASC,
            general.Usuarios_menu_profile.z_index ASC
MYSQL;
        $query = $em->createNativeQuery($sql, $rsm);
        $query->setParameter("user_id", $userId);

        $data = $query->getResult();
        return $data;
    }
Originally created by @JorgeSivil on GitHub (Jun 1, 2017). Originally assigned to: @Ocramius on GitHub. I spent 2 hours debugging why this was returning me 139 results all null. It was that a column had one uppercase char in the name and it was different from the value passed to addFieldResult. This should throw an exception! This is really very hard to debug. `$rsm->addFieldResult('umpp', 'ID_menu', 'id'); was $rsm->addFieldResult('umpp', 'ID_Menu', 'id');` And then the unexpected result, which is partly correct, because there were 139 rows, but all null. This is the complete code (I know the table and SQL is ugly, I just copy pasted what client gave me) ``` public function getEntriesForUserId($userId): array { $em = $this->getEntityManager(); $rsm = new ResultSetMapping(); $rsm->addEntityResult('AppBundle\Entity\LegacyMenuEntry', 'umpp'); $rsm->addFieldResult('umpp', 'ID_menu', 'id'); $rsm->addFieldResult('umpp', 'Nombre', 'nombre'); $rsm->addFieldResult('umpp', 'Path_logico', 'path_logico'); $rsm->addFieldResult('umpp', 'Path_real', 'path_real'); $rsm->addFieldResult('umpp', 'Frame_destino', 'frame_destino'); $rsm->addFieldResult('umpp', 'Img_cerrado', 'img_cerrado'); $rsm->addFieldResult('umpp', 'Img_abierto', 'img_abierto'); $rsm->addFieldResult('umpp', 'z_index', 'z_index'); $sql = <<<MYSQL SELECT umpp.* FROM general.Usuarios_legajo_profile JOIN general.Usuarios_menu_profile ON general.Usuarios_legajo_profile.ID_profile = general.Usuarios_menu_profile.ID_profile AND Usuarios_legajo_profile.Usuario = :user_id INNER JOIN general.Usuarios_menu_path umpp ON general.Usuarios_menu_profile.ID_menu = umpp.ID_menu ORDER BY general.Usuarios_legajo_profile.z_index ASC, general.Usuarios_menu_profile.z_index ASC MYSQL; $query = $em->createNativeQuery($sql, $rsm); $query->setParameter("user_id", $userId); $data = $query->getResult(); return $data; } ```
admin added the Won't Fix label 2026-01-22 15:11:23 +01:00
admin closed this issue 2026-01-22 15:11:23 +01:00
Author
Owner

@Ocramius commented on GitHub (Jun 1, 2017):

This should throw an exception!

There is no way for the code to throw an exception here, as the query would have to be executed first, and there's no guarantee about the format of the retrieved data (it's native SQL, remember? Anything can happen here...)

@Ocramius commented on GitHub (Jun 1, 2017): > This should throw an exception! There is no way for the code to throw an exception here, as the query would have to be executed first, and there's no guarantee about the format of the retrieved data (it's native SQL, remember? Anything can happen here...)
Author
Owner

@JorgeSivil commented on GitHub (Jun 1, 2017):

@Ocramius I don't understand, the query is executed first. Then the data is mapped to an object. Then the object is null. Why you can't throw an exception if you notice that you are returning null? Why is it different to have a completely useless array (all the members null) than having an exception thrown with a friendly debug message?

I mean, if I would had the message "The field X is not mapped anywhere" would have pointed me in the right direction to fix it. And would have saved me 2 hours of going through the immense calls with F7 to reach the return null and see which field was the problem.

@JorgeSivil commented on GitHub (Jun 1, 2017): @Ocramius I don't understand, the query is executed first. Then the data is mapped to an object. Then the object is null. Why you can't throw an exception if you notice that you are returning null? Why is it different to have a completely useless array (all the members null) than having an exception thrown with a friendly debug message? I mean, if I would had the message "The field X is not mapped anywhere" would have pointed me in the right direction to fix it. And would have saved me 2 hours of going through the immense calls with F7 to reach the return null and see which field was the problem.
Author
Owner

@Ocramius commented on GitHub (Jun 1, 2017):

@JorgeSivil there is no guarantee by the underlying layer that a low-level operation always returns the same fields at every iteration.

When dealing with the RSM, you are already playing with fire (that's what the component is for!), so if we add a limitation here, we likely introduce a bug for other consumers that use dynamic SQL.

@Ocramius commented on GitHub (Jun 1, 2017): @JorgeSivil there is no guarantee by the underlying layer that a low-level operation always returns the same fields at every iteration. When dealing with the RSM, you are already playing with fire (that's what the component is for!), so if we add a limitation here, we likely introduce a bug for other consumers that use dynamic SQL.
Author
Owner

@JorgeSivil commented on GitHub (Jun 1, 2017):

@Ocramius can you consider adding a wrapper class/method that throws exceptions for this case? Or a config parameter to set this behaviour?

Thanks for the reply

@JorgeSivil commented on GitHub (Jun 1, 2017): @Ocramius can you consider adding a wrapper class/method that throws exceptions for this case? Or a config parameter to set this behaviour? Thanks for the reply
Author
Owner

@Ocramius commented on GitHub (Jun 1, 2017):

No, this subsystem is already complex and configurable enough, and the fact that you just experienced it at your own expense is a direct demonstration of that.

As much as I also hate having dynamic fields and things that fail when something is missing by accident, this part is fragile by design. It glues together squishy SQL results with stronger-typed PHP objects and scalar fields. Mistakes will happen here, but tests (especially integration tests) are required when using this functionality.

This won't be improved, sorry.

Closing as won't fix

@Ocramius commented on GitHub (Jun 1, 2017): No, this subsystem is already complex and configurable enough, and the fact that you just experienced it at your own expense is a direct demonstration of that. As much as I also hate having dynamic fields and things that fail when something is missing by accident, this part is fragile by design. It glues together squishy SQL results with stronger-typed PHP objects and scalar fields. Mistakes will happen here, but tests (especially integration tests) are required when using this functionality. This won't be improved, sorry. Closing as `won't fix`
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#5564