views:

127

answers:

2

hi,

I have problems with my coredata sqlite DB, which hosts a book DB. After a crash a user experiences has the problem, that the data isn't shown properly any more in his/her tableview.

This is due to the fact, that the performFetch method returns an error:

[NSFetchedResultsController deleteCacheWithName:nil];
if (![[self fetchedResultsController] performFetch:&error]) { 
        //NSArray *array = [[self fetchedResultsController] fetchedObjects];
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
#ifdef DEBUG
        abort();
#endif
    }

which results in this error message:

Unresolved error Error Domain=NSCocoaErrorDomain Code=134060 "The operation couldn’t be completed. (Cocoa error 134060.)" UserInfo=0x1dff80 {reason=The fetched object at index 312 has an out of order section name 'Z. Objects must be sorted by section name'}, { reason = "The fetched object at index 312 has an out of order section name 'Z. Objects must be sorted by section name'";

When I have a look into the sqlite file with 'SQLite Database Browser 2.0 b1' the attributes of each entity seem to be ok.

When I delete some of the entities being mentioned everything works fine again.

I would like to know how I can find out what exactly is wrong with the mentioned entities and fix that, so the user can use his/her data again. Of course I want to fix the bug which causes the malformed DB as well but that is out of focus in this post.

Does anybody have any hints where I could have a look at or what might be malformed within my DB or what "an out of order section name" is?

This is the code for my fetchedResultsController:

- (NSFetchedResultsController *)fetchedResultsController {

    if (fetchedResultsController != nil) {
        return fetchedResultsController;
    }


    // Create the fetch request for the entity.
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    // Edit the entity name as appropriate.
    NSEntityDescription *entity;
    entity = [NSEntityDescription entityForName:@"Book" inManagedObjectContext:[[GlobalData sharedInstance] managedObjectContext]];
    [fetchRequest setEntity:entity];

    // Set the batch size to a suitable number.
    [fetchRequest setFetchBatchSize:10];

    //set searchPredicate
    NSPredicate *predicate = nil;
    if (self.bibList != nil) {
        predicate = [NSPredicate predicateWithFormat:@"ANY BibLists.name LIKE %@", self.bibList.name];      
    }

    if (predicate) {
        [fetchRequest setPredicate:predicate];
    }


    // Edit the sort key as appropriate.
    NSSortDescriptor *sortDescriptor;   
    NSString *sortDescriptorString = nil;       
    sortDescriptorString = @"title";
    sortDescriptor = [[NSSortDescriptor alloc] initWithKey:sortDescriptorString ascending:YES]; 
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];   
    [fetchRequest setSortDescriptors:sortDescriptors];

    NSString *sectionNameKeyPath = nil;
    sectionNameKeyPath = @"uppercaseFirstLetterOfTitle";

    // Edit the section name key path and cache name if appropriate.
    // nil for section name key path means "no sections".
    NSFetchedResultsController *aFetchedResultsController;
    aFetchedResultsController = [[NSFetchedResultsController alloc] 
                             initWithFetchRequest:fetchRequest 
                             managedObjectContext:[[GlobalData sharedInstance] managedObjectContext] 
                             sectionNameKeyPath:sectionNameKeyPath 
                             cacheName:@"Bibliography"];

    aFetchedResultsController.delegate = self;
    self.fetchedResultsController = aFetchedResultsController;

    [aFetchedResultsController release];
    [fetchRequest release];
    [sortDescriptor release];
    [sortDescriptors release];

    return fetchedResultsController;
}    

Thanks b00tsy

A: 

The following statement in the documentation hints at the error.

If this key path is not the same as that specified by the first sort descriptor in fetchRequest, they must generate the same relative orderings.

It seems that the item at index 312 is not in the correct section when sorting on title alone. This could be a result of the title property beginning with a non-letter or lowercase letter, which using lexical sorting would put it after the Z section but the value of the item's uppercaseFirstLetterOfTitle is not 'Z'. As a suggestion, try adding a sort descriptor on the uppercaseFirstLetterOfTitle as the first sort descriptor, then add the sort descriptor on title.

falconcreek
Thanks for your response. That's having lowercase letters as titles is exactly the problem. But as `uppercaseFirstLetterOfTitle` is a transient property I didn't manage to use it as a sort descriptor. Should I make it persistent or is there another option to handle that case? Or should I check on title generation that it starts with an uppercase letter?
b00tsy
As per TechZen, you can drop the `uppercaseFirstLetterOfTitle`. The default behaviour of passing the `@title` to `sectionNameKeyPath:` will provide the behaviour that you want. It is not equivalent to "group by title" in SQL
falconcreek
well that leads to a section for each single title, where I would like to have a section for each first letter only. Or am I missing s.th. out?
b00tsy
you need to use the `NSFetchedResultsSectionInfo` protocol methods in your implementation of the `UITableViewDataSourceDelegate` protocol methods.
falconcreek
You mean I should manually generate the sections / sectionindextitles and so on? At the moment I force all titles to have uppercase first letters. That works, but I want to prevent using that method, cause I also sort for LastNames (of authors) in another view which causes problems with aristocratic names like "von / van / ..."
b00tsy
A: 

I think what is going on here is that your SQL store is corrupted and/or you have an unusual sectionNameKeyPath.

The Core Data schema prefixes all the SQL column names with Z so an error of a name of 'Z suggest a corrupted SQL table. To test for that, execute the fetch request directly instead of using the fetched results controller and see if you can fetch all the objects.

If you can't, then the SQL store is corrupted. Most likely, some table component is simply name 'Z instead of something like ZAttributeName. You will have to edit the SQL directly but that is tricky because of the custom private schema. See this post to get an idea what to look for. There are not any tools for doing this because corruption of the store is so rare.

If the fetch works then the problem is with the sectionNameKeyPath being handed to the fetched results controller. Right now, it looks like you have an entity attribute with the first letter of the title attribute. (This is redundant because by default, the FRC will automatically return alphabetic sections based on any string attribute.) Try changing the sectionNameKeyPath to just the title attribute name (most likely "title"). In any case, just dispense with the uppercaseFirstLetterOfTitle attribute altogether.

TechZen
the uppercaseFirstLetterOfTitle attribute *can* be useful when working with large datasets as suggested by Apple in the WWDC Session 118 video. Potential to improve performance with added complexity though. (This post is clear evidence.)
falconcreek
That explains why I keep seeing this. I think a lot of people are using it unnecessarily. It's not a technique for novices.
TechZen
Thanks for your response. The direct fetchRequest on the managedObjectContext worked. The problem was that there are lowercase titles and non-ascii titles available which caused the problem mentioned by falconcreek. Dispensing with `uppercaseFirstLetterOfTitle` is no option, cause I don't want a section for every title. I want a section for every `uppercaseFirstLetterOfTitle ` only.
b00tsy
Can you include code that shows using the default sections from FRC that displays the tableview sections and index like the Contacts app?
falconcreek