Issue Details (XML | Word | Printable)

Key: NUCCORE-693
Type: Bug Bug
Status: Closed Closed
Resolution: Fixed
Priority: Major Major
Assignee: Andy Jefferson
Reporter: Guido Anzuoni
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
DataNucleus Core

Get object by id (app identity) does not check if the real class in the datastore for the id matches the requested class

Created: 08/Apr/11 02:42 PM   Updated: 09/May/11 09:58 AM   Resolved: 10/Apr/11 09:45 AM
Component/s: None
Affects Version/s: None
Fix Version/s: 3.0.0.m4

File Attachments: 1. Zip Archive jira693.zip (3 kB)



 Description  « Hide
class A {
long id;
}
class B extends A {

}
class C extends A {

}
If I have this database rows:
----------------------
ID CLASS
1 B
2 C

Now if I call (JPA):
em.find(B.class, new Long(2))
DN returns a Java instance of B class while in the DB the instance with id=2 is of class C.

If I execute a query like
select b from B b where b.id = 2
DN returns correct empty result
If I do:
   select c from C c where c.id = 2
and then
   em.find(B.class, new Long(2))

I have two different java object instances.
A, B, C are mappend onto the same table.

Sort Order: Ascending order - Click to sort in descending order
Andy Jefferson added a comment - 08/Apr/11 08:31 PM
If i call em.find(B.class, Long(2)) I get SQL like the following
SELECT `A0`.`TYPE` FROM `A` `A0` WHERE ((`A0`.`TYPE` = 'B' OR `A0`.`TYPE` = 'C')) AND `A0`.`ID` = <2>
and it uses the 'TYPE' to see that it is a C, and creates a C object. No problems with the latter route either.

Guido Anzuoni added a comment - 08/Apr/11 10:40 PM - edited
Hmmm, I have different results.
I thought that the difference could be cause by A being a mapped superclass but no, it is not the case.....
I get your same result in case of
em.find(A.class, new Long(2);

Guido Anzuoni added a comment - 08/Apr/11 11:44 PM
If I modify RDBMSStoreManager at line 946 into:

            AbstractClassMetaData cmd = getMetaDataManager().getMetaDataForClass(className, clr);
            cmd = cmd.getBaseClassManagingTable();
where (in AbstractClassMetaData )
    public AbstractClassMetaData getBaseClassManagingTable()
    {
        if (getInheritanceMetaData().getStrategy() == InheritanceStrategy.NEW_TABLE)
        {
            return this;
        }
        AbstractClassMetaData cmd = getSuperclassManagingTable();
        return cmd;
    }

I get:
java.lang.ClassCastException: org.datanucleus.test.CClass cannot be cast to org.datanucleus.test.BClass
at org.datanucleus.test.MainOID.main(MainOID.java:34)
if I do
B b = em.find(B.class, new Long(2))
while if I do
Object b = em.find(B.class, new Long(2))
b.getClass() == C.class !!!!

Maybe the should apply in the case :
if (id instanceof OID) at line 931

but this is for RDBMS. And the other store ? DB4O, Excel etc ?

Last but not least, what would be the right outcome ? ClassCastException, null ?

Guido Anzuoni added a comment - 09/Apr/11 12:08 AM
core patch adds the method to get the base class managing a table in a hierarchy and
in ObjectManagerImpl there is the check to verify that the class indicated in the requested id is assignable from the class of the real object that would be returned to the app.
In case of mismatch NucleusObjectNotFoundException is raised

Guido Anzuoni added a comment - 09/Apr/11 01:07 AM
Added method in AbstractClassMetaData was not considering all the possible inheritance strategies

Andy Jefferson added a comment - 09/Apr/11 06:26 AM
What about a test to demonstrate this? Prerequisite to any changes

Andy Jefferson added a comment - 10/Apr/11 09:45 AM
The error is actually in the block of code that sees no subclasses of BClass, and assumes that nothing else is needed. But in this case (and some other ones that are introduced by allowing table sharing) there are other instances in the table, so we force a check.