CoreData returns objects not in RHS of IN pred if from IDs from valueForKeyPath:

Originator:chaos42
Number:rdar://12194503 Date Originated:28-Aug-2012 07:42 PM
Status:Open Resolved:
Product:iPhone SDK Product Version:5.1
Classification:Serious Bug Reproducible:Always
 
Summary: If the right hand side of an IN predicate contains managed objects of different entities collected via -objectWithID: from an array of NSManagedObjectIDs collected via another fetch request and then calling -valueForKeyPath:@"relationship.objectID" on the results , the NSManagedObjectContext will, on calling -executeFetchRequest:error:, return objects that are not in the RHS if they have the same primary key [PK] (last path component of the NSManagedObjectID URI) as a managed object of a different entity which *is* in the RHS.  

Steps to Reproduce:
See the attached sample project that demonstrates this bug.

Expected Results: See log output - there should be no results. More generally, fetch request results with an IN predicate should always be limited to the contents of the RHS of that predicate 

Actual Results: See log output - there are 10*n results (where n is the number of times the program has been run since reseting the simulator). More generally, they are not. 

Regression: Unknown. 

Notes: Ignore CDRSearchOperation. I originally thought the minimum reproduction step required getting the objectIDs from another thread, but it doesn't. 

Relevant code, since Open Radar doesn't accept attachments (I'll upload to Dropbox later):

in -applicationDidFinishLaunching:

    NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:40];
    for (int i = 0; i < 10; i++)
    {
        Foo *foo = [NSEntityDescription insertNewObjectForEntityForName:@"Foo" inManagedObjectContext:[self managedObjectContext]];
        foo.name = @"Hello";
        Keyword *fooword = [NSEntityDescription insertNewObjectForEntityForName:@"Keyword" inManagedObjectContext:[self managedObjectContext]];
        fooword.keyword = foo.name;
        fooword.foo = foo;
        [array addObject:foo];
        
        Bar *bar = [NSEntityDescription insertNewObjectForEntityForName:@"Bar" inManagedObjectContext:[self managedObjectContext]];
        bar.name = @"goodbye";
        Keyword *barword = [NSEntityDescription insertNewObjectForEntityForName:@"Keyword" inManagedObjectContext:[self managedObjectContext]];
        barword.keyword = bar.name;
        barword.bar = bar;
        [array addObject:bar];
        
        Baz *baz = [NSEntityDescription insertNewObjectForEntityForName:@"Baz" inManagedObjectContext:[self managedObjectContext]];
        baz.name = @"You say";
        Keyword *bazword = [NSEntityDescription insertNewObjectForEntityForName:@"Keyword" inManagedObjectContext:[self managedObjectContext]];
        bazword.keyword = @"You";
        bazword.baz = baz;
        Keyword *bazword2 = [NSEntityDescription insertNewObjectForEntityForName:@"Keyword" inManagedObjectContext:[self managedObjectContext]];
        bazword2.keyword = @"Say";
        bazword2.baz = baz;
        [array addObject:baz];
        
        Qux *qux = [NSEntityDescription insertNewObjectForEntityForName:@"Qux" inManagedObjectContext:[self managedObjectContext]];
        qux.name = @"I say";
        Keyword *quxword = [NSEntityDescription insertNewObjectForEntityForName:@"Keyword" inManagedObjectContext:[self managedObjectContext]];
        quxword.keyword = @"I";
        quxword.qux = qux;
        Keyword *quxword2 = [NSEntityDescription insertNewObjectForEntityForName:@"Keyword" inManagedObjectContext:[self managedObjectContext]];
        quxword2.keyword = @"Say";
        quxword2.qux = qux;
        [array addObject:qux];

    }
    [[self managedObjectContext] save:nil];

    //Uncomment this to see what happens normally
    
//    NSFetchRequest *fooRequest = [[NSFetchRequest alloc] initWithEntityName:@"Foo"];
//    NSFetchRequest *barRequest = [[NSFetchRequest alloc] initWithEntityName:@"Bar"];
//    NSFetchRequest *bazRequest = [[NSFetchRequest alloc] initWithEntityName:@"Baz"];
//    NSFetchRequest *quxRequest = [[NSFetchRequest alloc] initWithEntityName:@"Qux"];
//    NSMutableArray *oids = [[NSMutableArray alloc] init];
//    [oids addObjectsFromArray:[[[self managedObjectContext] executeFetchRequest:fooRequest error:nil] valueForKey:@"objectID"]];
//    [oids addObjectsFromArray:[[[self managedObjectContext] executeFetchRequest:barRequest error:nil] valueForKey:@"objectID"]];
//    [oids addObjectsFromArray:[[[self managedObjectContext] executeFetchRequest:bazRequest error:nil] valueForKey:@"objectID"]];
//    [oids addObjectsFromArray:[[[self managedObjectContext] executeFetchRequest:quxRequest error:nil] valueForKey:@"objectID"]];
//    NSMutableArray *good = [[NSMutableArray alloc] initWithCapacity:[oids count]];
//    for (NSManagedObjectID *oid in oids)
//    {
//        [good addObject:[[self managedObjectContext] objectWithID:oid]];
//    }
//    NSLog(@"the array of NSMOIDs: %@", good);
//    NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"Foo"];
//    request.predicate = [NSPredicate predicateWithFormat:@"self IN %@", good];
//    NSArray *results = [[self managedObjectContext] executeFetchRequest:request error:nil];
//    NSLog(@"Expected: %@\n\n Actual: %@", [good filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"entity = %@", [NSEntityDescription entityForName:@"Foo" inManagedObjectContext:[self managedObjectContext]]]], results);
    
    
    NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"Keyword"];
    request.predicate = [NSComparisonPredicate predicateWithLeftExpression:[NSExpression expressionForKeyPath:@"keyword"]
                                                           rightExpression:[NSExpression expressionForConstantValue:@"Say"] 
                                                                  modifier:NSDirectPredicateModifier 
                                                                      type:NSContainsPredicateOperatorType
                                                                   options:NSCaseInsensitivePredicateOption];
    NSArray *results = [[self managedObjectContext] executeFetchRequest:request error:nil];
    NSMutableSet *resultSet = [NSMutableSet set];
    [resultSet addObjectsFromArray:[results valueForKeyPath:@"foo.objectID"]];
    [resultSet addObjectsFromArray:[results valueForKeyPath:@"bar.objectID"]];
    [resultSet addObjectsFromArray:[results valueForKeyPath:@"baz.objectID"]];
    [resultSet addObjectsFromArray:[results valueForKeyPath:@"qux.objectID"]];
    [resultSet removeObject:[NSNull null]];
    [self searchOperation:nil didReturnResults:[resultSet allObjects]];
    ...
}


- (void)searchOperation:(id)op didReturnResults:(NSArray *)results
{
    NSFetchRequest *expReq = [[NSFetchRequest alloc] initWithEntityName:@"Foo"];
    expReq.predicate = [NSComparisonPredicate predicateWithLeftExpression:[NSExpression expressionForKeyPath:@"keywords.keyword"] 
                                                          rightExpression:[NSExpression expressionForConstantValue:@"Say"]
                                                                 modifier:NSAnyPredicateModifier 
                                                                     type:NSContainsPredicateOperatorType 
                                                                  options:NSCaseInsensitivePredicateOption];
    NSArray *expLoggit = [[self managedObjectContext] executeFetchRequest:expReq error:nil];
    NSLog(@"Expected Results (should be empty, becuase the only keywords that have \"foo\"s have \"Hello\" as a keyword: %@", expLoggit);

    
    NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:[results count]];
    for (NSManagedObjectID *oid in results)
    {
        [array addObject:[[self managedObjectContext] objectWithID:oid]];
    }
    NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"Foo"];
    request.predicate = [NSPredicate predicateWithFormat:@"self IN %@", array];
    NSArray *loggit = [[self managedObjectContext] executeFetchRequest:request error:nil];
    NSLog(@"Actual Results:%@", loggit);
}

Comments


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!