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
DDC-1389: Querying subclass entities using DQL results in broken SQL being generated #2009
Comments
Comment created by @beberlei: Assigned to guilherme |
Comment created by dalvarez: Here is a little incentive: I will sponsor 200 € to an account of Ben's choice, if this issue gets fixed before Sunday morning, October, 16th, 2011, 12 o'clock a. m. (GMT). The award will be paid immediately after job completion and provision of a patched 2.1.2 version. |
Comment created by marcoalbarelli: This bug affected also me: the ->findAll() method worked As a workaround I copy/pasted all parent fields (+getters/setters) in the child class and removed the inheritance
|
Comment created by @beberlei: This is fixed in master already, i am digging out the patch from the history, Guilherme committed that while i was in holidays the last weeks. |
Comment created by @beberlei: Are you sure your query has a WHERE condition? There is a bug where this happens when no WHERE condition is specified. |
Comment created by @beberlei: @daniel: I think your mapping is wrong as well, or the ClassMetadata of your "A" lost the information that its Joined Inheritance. Can you verify?
There is only a single place where this can happen in "walkFromClause" and it handles this correctly:
|
Comment created by marcoalbarelli: @benjamin:
If I use that definition template renderings always fails this way:
Line 36 of that template is:
Relevant PDO stacktrace:
Definition of the parent Tag class
I didn't specify any explicit inheritanceType nor any discriminatorMapping since I thought Doctrine would ahve taken care of those details with its own defaults, right? P.S. |
Comment created by @beberlei: @marco: You are missing the @Orm\InheritanceType and @Orm\DiscriminatorMap properties, see the documentation on Inheritance: http://www.doctrine-project.org/docs/orm/2.0/en/reference/inheritance-mapping.html This is probably a good point to make the error messages better. |
Comment created by @beberlei: Hm you probably have this proerpties. Can you give me a full show of the Tag class aswell? I need to reproduce this. The problem is that all the unit-tests with examples of this work perfectly. So there has to be a special case somewhere that we have overlooked so far. |
Comment created by marcoalbarelli: There you go:
I just added the
annotation and it works, even though I'm not very happy about this approach since this way I have to hardcode inheritance information in the parent class I saw the documentation, but as I said before I thought Doctrine would have filled in the blanks on its own, as it does in many other repects (table and column names for one) P.S. |
Comment created by dalvarez: I will quickly prepare the actual data to give you a complete picture, and come back to you in a couple of minutes. |
Comment created by dalvarez: There was an occassion of a missing DiscriminatorMap in my case, too, though at a different place that I originally referred to when posting the issue. There was nothing that would have hinted me at that... Some basic validation would be helpful here. After fixing that, the problem now manifests in PHP warnings and a subsequent fatal error. Warning: class_parents(): object or string expected in /var/www/invoiceCreator/src/thirdPartyComponents/doctrine/Doctrine/ORM/Mapping/ClassMetadataFactory.php on line 224 Warning: array_reverse() expects parameter 1 to be array, boolean given in /var/www/invoiceCreator/src/thirdPartyComponents/doctrine/Doctrine/ORM/Mapping/ClassMetadataFactory.php on line 224 Warning: Invalid argument supplied for foreach() in /var/www/invoiceCreator/src/thirdPartyComponents/doctrine/Doctrine/ORM/Mapping/ClassMetadataFactory.php on line 224 Fatal error: Uncaught exception 'ReflectionException' with message 'Class does not exist' in /var/www/invoiceCreator/src/thirdPartyComponents/doctrine/Doctrine/ORM/Mapping/ClassMetadata.php:66 Stack trace: #0 /var/www/invoiceCreator/src/thirdPartyComponents/doctrine/Doctrine/ORM/Mapping/ClassMetadata.php(66): ReflectionClass->__construct('') #1 /var/www/invoiceCreator/src/thirdPartyComponents/doctrine/Doctrine/ORM/Mapping/ClassMetadataFactory.php(364): Doctrine\ORM\Mapping\ClassMetadata->__construct(NULL) #2 /var/www/invoiceCreator/src/thirdPartyComponents/doctrine/Doctrine/ORM/Mapping/ClassMetadataFactory.php(265): Doctrine\ORM\Mapping\ClassMetadataFactory->newClassMetadataInstance(NULL) #3 /var/www/invoiceCreator/src/thirdPartyComponents/doctrine/Doctrine/ORM/Mapping/ClassMetadataFactory.php(170): Doctrine\ORM\Mapping\ClassMetadataFactory->loadMetadata(NULL) #4 /var/www/invoiceCreator/src/thirdPartyComponents/doctrine/Doctrine/ORM/EntityManager.php(257): Doctrine\ORM\Mapping\ClassMetadataFactory->getMetadataFor(NULL) # in /var/www/invoiceCreator/src/thirdPartyComponents/doctrine/Doctrine/ORM/Mapping/ClassMetadata.php on line 66 This is probably unrelated to the original issue, but it is equally blocking my use case. I debugged the entity name that gets passed to the constructor as an argument. The class that gets passed to the ORM\Mapping\ClassMetadata constructor is an Entity that inherits from a mapped superclass and implements an interface. It does not have any other parents except for the mapped superclass though. Any ideas on this? I could debug it further and try to break this down to a more isolated case, too, if necessary. |
Comment created by dalvarez: From the stack trace it can be seen, that ORM\EntityManager->getClassMetadata() gets called with a NULL argument. Unfortunately, the stack trace ends there. I have thrown an exception from that point to complete it (don't worry about the exception, I willingly threw it, but it illustrates the call graph): Fatal error: Uncaught exception 'Exception' in /var/www/invoiceCreator/src/thirdPartyComponents/doctrine/Doctrine/ORM/EntityManager.php:263 Stack trace: #0 /var/www/invoiceCreator/src/thirdPartyComponents/doctrine/Doctrine/ORM/UnitOfWork.php(1862): Doctrine\ORM\EntityManager->getClassMetadata(NULL) #1 /var/www/invoiceCreator/src/thirdPartyComponents/doctrine/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php(139): Doctrine\ORM\UnitOfWork->createEntity(NULL, Array, Array) #2 /var/www/invoiceCreator/src/thirdPartyComponents/doctrine/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php(44): Doctrine\ORM\Internal\Hydration\SimpleObjectHydrator->_hydrateRow(Array, Array, Array) #3 /var/www/invoiceCreator/src/thirdPartyComponents/doctrine/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php(99): Doctrine\ORM\Internal\Hydration\SimpleObjectHydrator->_hydrateAll() #4 /var/www/invoiceCreator/src/thirdPartyComponents/doctrine/Doctrine/ORM/Persisters/BasicEntityPersister.php(580): Doctrine\ORM\Internal\Hydration\AbstractHy in /var/www/invoiceCreator/src/thirdPartyComponents/doctrine/Doctrine/ORM/EntityManager.php on line 263 Here is the statement that gets hydrated at that point: SELECT t1.totalPurchaseValueRegularVATRate AS totalPurchaseValueRegularVATRate2, t1.totalPurchaseValueReducedVATRate AS totalPurchaseValueReducedVATRate3, t1.sum AS sum4, t1.dbID AS dbID5, t0.grandTotal AS grandTotal6, t0.vatTotal_dbID AS vatTotal_dbID7, t1.doctrineTypeDiscriminator FROM GrandInvoiceTotal t0 INNER JOIN InvoiceTotal t1 ON t0.dbID = t1.dbID WHERE t1.dbID = ? Here is the Entity class definition for InvoiceTotal:
And the entity class definition for GrandInvoiceTotal:
Here is the mapped superclass "DataObject":
|
Comment created by dalvarez: What I am ultimately doing at that point, is calling EntityManager::remove() on an entity, which performs a cascading delete, which in turn deletes another entity, which in turn deletes another entity, and so on, up to the point where it starts to delete instances of InvoiceTotal and GrandInvoiceTotal. I just do not know what to do at this point because all of this is so involved into the inner workings of Doctrine 2. To me it seems that the cascading delete somehow entangles itself, somehow ending up with a read statement that it cannot process properly, because it cannot find some metadata it requires. Since the Discriminator Map is in place, what else could be missing? Of course, I have regenerated the proxies and updated the database, to reflect the latest changes. |
Comment created by dalvarez: I believe the original bug simply consisted in non-existent validation, leading to the possibility broken SQL being generated. But even with the Discriminator Map present where it was previously missing, I still cannot perform a cascading delete due to the crashing problem described above. I am still sticking to my incentive of sponsoring 200 €, if my use case gets working today, which is basically performing a cascading delete on the data shown above. Right now the problem has unfortunately just shifted. |
Comment created by dalvarez: I can provide you with additional sourcecode at any time, if you require it, or live contact and access to a testing system. |
Comment created by dalvarez: Here is a complete stack trace: #0 /var/www/invoiceCreator/src/thirdPartyComponents/doctrine/Doctrine/ORM/Mapping/ClassMetadataFactory.php(364): Doctrine\ORM\Mapping\ClassMetadata->**construct(NULL) |
Comment created by dalvarez: Now I found the root cause. In the database, the doctrineTypeDiscriminator column was not initialized. Apparently, after adding the discriminator map to the entity class InvoiceTotal, the schema-tool:update command added the column, but never initialized it. This means that if a discriminator map is added to already existing entities, all instances existing in the database must be manually initialized with discriminator values. This is a classic pain in the arse, but thinking about it, it is logical, because the update command has nothing to do with data, it just manipulates the database schema. So conceptually, it is clean. I'll post another update once it finally works, because right now I the cascading delete still runs into an integrity constraint violation error. |
Comment created by dalvarez: Up to now I have not been able to reproduce the integrity constraint violation in an isolated test case. It is there, reliably reproducible, but only in the context of the application. I know which constraint fails, and when, but not why. It should not. The only possibility is that things get deleted in the wrong order. I will close this as invalid, because anyway, technically it is not the original issue. I will go on to write a manual delete routine to avoid the cascading delete. Maybe on the way I will find out what exactly caused the integrity constraint violation. |
Issue was closed with resolution "Invalid" |
Comment created by @beberlei: Added validation for this inside the hydrators. Hydration will now fail if a discriminator value is an empty string (0 is still allowed). |
Jira issue originally created by user dalvarez:
For entities that are instances of an entity class that inherits from another entity class, querying them directly using DQL will generate a broken SQL statement in which a table alias is referenced but never declared.
Here is an example SQL query:
SELECT a from \A a where a.someField = 1
Assume that A is an entity class that inherits from another entity class, say B.
The generated query will then be of the following (wrong) form:
SELECT i0_.someField as someField0, a1_.someOtherfield AS someOtherfield1 FROM A a1_ WHERE a1_.someField = 1 LIMIT 1
You can see that the generated query references two table aliases in the select list (i0* and a1_), but only declares one (a1*). This can't possibly work. Apparently the inheritance join does not make it into SQL. As inheritance joins are documented to happen transparently, I believe this is unintended behaviour.
The error message is then, of course, something like this:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'i0_.somefield' in 'field list'' ...
Strangely, it also happens for fields that are replicated to the subclass, e. g. the primary key.
Querying entity classes that do not inherit from other entity classes works as expected though, including the base classes in an entity inheritance hierarchy.
The text was updated successfully, but these errors were encountered: