Issue Details (XML | Word | Printable)

Key: NUCRDBMS-543
Type: Bug Bug
Status: Closed Closed
Resolution: Duplicate
Priority: Major Major
Assignee: Unassigned
Reporter: Richard DiCroce
Votes: 0
Watchers: 1
Operations

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

Running contains() on a set of interfaces returns false when it should return true

Created: 09/Jul/11 07:36 AM   Updated: 01/Aug/11 05:13 PM   Resolved: 10/Jul/11 09:09 AM
Component/s: None
Affects Version/s: 3.0.0.m5
Fix Version/s: None

File Attachments: 1. Zip Archive ContainsTestCase.zip (4 kB)


Datastore: MySQL


 Description  « Hide
Take two classes, FooA and FooB, both of which implement IFoo and contain a Set<IFoo>. Now create an instance of FooA and an instance of FooB, then add the FooB to the FooA's set and persist it. Clear your caches (or close your program and reopen it), then query the datastore and find the instance of FooA you just persisted. Get the FooA's set of IFoo and ask if it contains the instance of FooB. The set will report that it does NOT contain the FooB, even though it actually does.

The problem appears to be related to caching and SCO wrappers. The set will correctly report that it contains the FooB if the instance of FooB is in the cache. I have, therefore, been able to work around this problem in two ways:
- marking the set as being part of the default fetch group
- calling the set's iterator() method before calling the set's contains() method

Sort Order: Ascending order - Click to sort in descending order
Richard DiCroce added a comment - 09/Jul/11 07:37 AM
Test case

Andy Jefferson added a comment - 09/Jul/11 09:13 AM
One would have thought that if you had a problem with a query you would actually look at the log and mention what is the SQL generated, and then define what is wrong with that SQL.

Richard DiCroce added a comment - 09/Jul/11 07:02 PM
This issue also affects removing items from the set. Having looked at the logs more closely, yes, it is a query problem.

For contains():
1136 DEBUG [main] DataNucleus.Datastore.Native - SELECT `FOOA_ID_OID` FROM `FOOA_STUFF` THIS WHERE THIS.`FOOA_ID_OID` = <1> AND THIS.`STUFF_FOOB_FOOB_ID_EID` = <1> AND THIS.`STUFF_FOOA_FOOA_ID_EID` = <null>

And for remove():
1143 DEBUG [main] DataNucleus.Datastore.Native - DELETE FROM `FOOA_STUFF` WHERE `FOOA_ID_OID` = <1> AND `STUFF_FOOB_FOOB_ID_EID` = <1> AND `STUFF_FOOA_FOOA_ID_EID` = <null>
1144 DEBUG [main] DataNucleus.Datastore.Persist - Execution Time = 1 ms (number of rows = 0)

The problem with the SQL is patently obvious: the parameters have brackets around them, but shouldn't.

Andy Jefferson added a comment - 09/Jul/11 07:23 PM
Nope, there are actually no <> around anything in what is sent to JDBC. They are displayed like that to show that it is a parameter.

Richard DiCroce added a comment - 09/Jul/11 10:17 PM
Ah, I see it now. Fascinating! I didn't know that you can't run a query with "x = NULL" and instead you have to use "x IS NULL".

Andy Jefferson added a comment - 10/Jul/11 09:02 AM
So why is your element "id" null? because you pass in a transient object to a query (JDOHelper.getObjectId(obj) == null). i.e user error.

Andy Jefferson added a comment - 10/Jul/11 09:09 AM
And if you say "well I have equals defined" then sure you do but you're using a backed collection and that goes to the datastore to evaluate such things, and if expecting otherwise you refer to http://www.datanucleus.org/servlet/jira/browse/NUCCORE-684

Richard DiCroce added a comment - 10/Jul/11 06:25 PM
Did you even bother to look at the test case? Or read anything else I've said?

First: I'll agree that NUCCORE-684 is similar, but it's not the same. Using the workaround specified there of doing collection=true and lazy=false solves the contains() issue, because it causes the objects to be loaded from the datastore with the rest of the object. I accomplished the same thing by making the Set part of the default fetch group. That workaround does NOT fix remove(), however.

Second: The ID isn't null because I'm passing a transient object, mostly because I'm NOT passing a transient object, which you would know if you had bothered to look at the test case:

FooA fooA = query.filter(QFooA.candidate().id.eq(1)).executeUnique();
FooB fooB = query2.filter(QFooB.candidate().id.eq(1)).executeUnique();
if (!fooA.getStuff().contains(fooB)) {
NucleusLogger.GENERAL.info(">> Contains() should have returned true, but returned false!");
}

It's null because the table being queried is a join relationship for a Set of IFoo, which is an interface. IFoo has two implementations: FooA and FooB, so DataNucleus has generated a join table with a column and corresponding foreign key for each implementation. So of course STUFF_FOOA_FOOA_ID_EID is null: I'm asking to remove (or asking if the set contains) an instance of FooB, not an instance of a class that is somehow magically inheriting from both FooA and FooB.

Andy Jefferson added a comment - 10/Jul/11 07:22 PM
Obviously I "bothered" to scan through the test case (since I "bothered" to refer to it), which I do in my spare time and get no thanks for. My initial assessment, based on the problem title, description and what I saw in the testcase is as I said (i.e the issue talks of contains(), and as you've already said, contains() works when you do as per the other issue. QED).

You introduce some other issue into this issue, later on (3 comments down), which is obviously not the same thing as contains() ... the title of this issue.

Obviously you could easily raise a specific issue for the remove problem, and link to this issue for the testcase, and then even, dare I suggest it, provide a patch to fix it.

Richard DiCroce added a comment - 10/Jul/11 08:10 PM
Okay, that was my bad: I spotted the potential problem of using a transient object after I submitted the test case and modified it locally on my machine so that it uses an instance retrieved from the datastore. The reason I didn't bother to upload a new version of the test case is because that didn't fix the problem.

The reason I brought up the remove() issue is because, although remove() is obviously not the same as contains(), the problems DO appear to be related: incorrect SQL is produced in both cases. Yes, the workaround in NUCCORE-684 happens to solve the problem, but that appears to be coincidence rather than because the underlying problem is actually the same issue.

Anyway, I'll adjust the test case to better show the problems and create a new issue.