Skip to content
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

Bug in iOS14 batch fetches #335

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 23 additions & 22 deletions Incremental Store/EncryptedStore.m
Original file line number Diff line number Diff line change
Expand Up @@ -3530,7 +3530,19 @@ -(id)expressionDescriptionTypeValue:(NSExpressionDescription *)expressionDescrip

*/
- (NSDictionary *)whereClauseWithFetchRequest:(NSFetchRequest *)request {
NSDictionary *result = [self recursiveWhereClauseWithFetchRequest:request predicate:[request predicate]];
NSPredicate *predicate = [request predicate];

// Perform substitution in predicate if substitutionVariables are available
SEL substitutionVariablesSelector = NSSelectorFromString(@"substitutionVariables");
if ([request respondsToSelector:substitutionVariablesSelector]) {
// See https://stackoverflow.com/questions/7017281/performselector-may-cause-a-leak-because-its-selector-is-unknown for explanation of this pattern
IMP imp = [request methodForSelector:substitutionVariablesSelector];
NSDictionary *(*func)(id, SEL) = (void *)imp;
NSDictionary *substitutionVariables = func(request, substitutionVariablesSelector);
predicate = [predicate predicateWithSubstitutionVariables:substitutionVariables];
}

NSDictionary *result = [self recursiveWhereClauseWithFetchRequest:request predicate:predicate];
NSString *query = result[@"query"];

if ([self entityNeedsEntityTypeColumn:request.entity]) {
Expand Down Expand Up @@ -3707,7 +3719,16 @@ - (void)bindWhereClause:(NSDictionary *)clause toStatement:(sqlite3_statement *)
NSArray *bindings = [clause objectForKey:@"bindings"];

[bindings enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// downconvert objects and objectIDs to concrete values
if ([obj isKindOfClass:[NSManagedObject class]]) {
obj = [obj objectID];
}

if ([obj isKindOfClass:[NSManagedObjectID class]]) {
// objects with temporary ids haven't been inserted yet, so look for an id we know will never match
obj = [obj isTemporaryID] ? @"-1" : [self referenceObjectForObjectID:obj];
}

// string
if ([obj isKindOfClass:[NSString class]]) {
const char* str = [obj UTF8String];
Expand All @@ -3732,19 +3753,6 @@ - (void)bindWhereClause:(NSDictionary *)clause toStatement:(sqlite3_statement *)
}
}

// managed object id
else if ([obj isKindOfClass:[NSManagedObjectID class]]) {
id referenceObject = [self referenceObjectForObjectID:obj];
sqlite3_bind_int64(statement, ((int)idx + 1), [referenceObject unsignedLongLongValue]);
}

// managed object
else if ([obj isKindOfClass:[NSManagedObject class]]) {
NSManagedObjectID *objectID = [obj objectID];
id referenceObject = [self referenceObjectForObjectID:objectID];
sqlite3_bind_int64(statement, ((int)idx + 1), [referenceObject unsignedLongLongValue]);
}

// date
else if ([obj isKindOfClass:[NSDate class]]) {
sqlite3_bind_double(statement, ((int)idx + 1), [obj timeIntervalSince1970]);
Expand Down Expand Up @@ -4145,15 +4153,8 @@ - (void)parseExpression:(NSExpression *)expression
*operand = @"?";
*bindings = [NSString stringWithFormat:[operator objectForKey:@"format"], value];
} else if ([value isKindOfClass:[NSManagedObject class]] || [value isKindOfClass:[NSManagedObjectID class]]) {
NSManagedObjectID * objectId = [value isKindOfClass:[NSManagedObject class]] ? [value objectID]:value;
*operand = @"?";
// We're not going to be able to look up an object with a temporary id, it hasn't been inserted yet
if ([objectId isTemporaryID]) {
// Just look for an id we know will never match
*bindings = @"-1";
} else {
*bindings = value;
}
*bindings = value;
}
else if (!value || value == [NSNull null]) {
*bindings = nil;
Expand Down
36 changes: 36 additions & 0 deletions exampleProjects/IncrementalStore/Tests/IncrementalStoreTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,42 @@ -(void)test_predicateEqualityComparisonUsingDates
}];
}

-(void)test_predicateForSelfInComparisonWithUnpersistedObjects {
NSArray *users = @[
[NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:context],
[NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:context]
];

NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"User"];
[request setPredicate:[NSPredicate predicateWithFormat:@"SELF IN %@", users]];

NSError *error;
NSArray<NSManagedObject *> *results = [context executeFetchRequest:request error:&error];
XCTAssertNotNil(results);
XCTAssertEqual([results count], 2);
}

-(void)test_batchFetchWithNestedContextsAndUnpersistedObjects {
NSManagedObjectContext *parentContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[parentContext setPersistentStoreCoordinator: coordinator];

NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[childContext setParentContext:parentContext];
NSManagedObject *user = [NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:childContext];

NSError *error;
BOOL saved = [childContext save:&error];
XCTAssertTrue(saved, @"Failed to save child context: %@", error);

NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"User"];
[request setFetchBatchSize:20];

NSArray<NSManagedObject *> *results = [childContext executeFetchRequest:request error:&error];
XCTAssertNotNil(results, @"Failed to fetch saved user from parent context: %@", error);
XCTAssertEqual([results count], 1);
XCTAssertEqualObjects([results[0] objectID], [user objectID]);
}

-(void)test_sumExpression {

[self createUsers:10];
Expand Down