Skip navigation.
 
mlRe: [Leopard, workaround] Core Data migration for Tiger files
FROM : Adam Swift
DATE : Wed Nov 07 00:09:39 2007

On Nov 6, 2007, at 12:17 PM, Pierre Bernard wrote:

> Adam,
>
> Thanks for clarifying this. I can find no word about this in the 
> documentation. This is a very serious limitation and should be 
> mentioned.


I think that's a fair point, please file a documentation enhancement 
request.

> Is there really no way to infer entity hashes from the tables 
> available in the SQLite store at hand?


Unfortunately, no.

> Wouldn't it be great if one could specify a default legacy model to 
> use on Tiger files?


Hm, maybe, it couldn't hurt to ask ;)

>
> Would you consider the below code to be preferable to the workaround 
> I posted earlier?


Sure, seems like the right way to go.  If there was a custom version 
number (or other custom key/value pair) assigned in your Tiger data 
store metadata you could verify that you're trying to open a 
recognized version of the store file, and from the nit-picky 
department, it'd be nice to revert the original file to it's original 
name (no tilde) if migration fails.

>
>
>     NSDictionary *sourceMetadata = [NSPersistentStoreCoordinator 
> metadataForPersistentStoreOfType:NSSQLiteStoreType URL:url 
> error:error];
>     
>     if (sourceMetadata == nil)
>     {
>         return NO;
>     }
>     
>     id versionHashes = [sourceMetadata 
> objectForKey:@"NSStoreModelVersionHashes"];
>     BOOL ok = NO;
>     
>     if (versionHashes != nil) {
>         NSMutableDictionary *newStoreOptions;
>         
>         if (storeOptions == nil)
>         {
>             newStoreOptions = [NSMutableDictionary dictionary];
>         }
>         else
>         {
>             newStoreOptions = [[storeOptions mutableCopy] autorelease];
>         }
>         
>         [newStoreOptions setObject:[NSNumber numberWithBool:YES] 
> forKey:NSMigratePersistentStoresAutomaticallyOption];
>         
>         ok = [super configurePersistentStoreCoordinatorForURL:url
>                                                       ofType:fileType
>                                           modelConfiguration:configuration
>                                                  storeOptions:newStoreOptions
>                                                         error:error];
>     }
>     else {
>         NSURL *modelURL = [NSURL fileURLWithPath:[Document 
> pathForModelNamed:@"HoudahSpot"]];
>         NSManagedObjectModel *sourceModel = [[NSManagedObjectModel alloc] 
> initWithContentsOfURL:modelURL];
>                 
>         NSManagedObjectModel *destinationModel = [self managedObjectModel];
>         
>         
>         /*
>          To perform the migration, we also need a mapping model.
>          We have the source and destination model; NSMapping model 
> provides a convenience method to find the correct mapping model from 
> a given array of bundles.
>          */
>         
>         NSArray *bundles = [NSArray arrayWithObject:[NSBundle mainBundle]];
>         
>         NSMappingModel *mappingModel = [NSMappingModel 
> mappingModelFromBundles:bundles forSourceModel:sourceModel 
> destinationModel:destinationModel];
>         
>         if (mappingModel == nil)
>         {
>             // should create a suitable NSError and set in 'error'
>             return NO;
>         }
>         
>         /*
>          Move the legacy file out of the way
>          */
>         NSString *originalPath = [url path];
>         NSString *legacyPath = [NSString stringWithFormat:@"%@~", 
> originalPath];
>         BOOL success = [[NSFileManager defaultManager] 
> movePath:originalPath toPath:legacyPath handler:nil];
>         
>         if (!success) {
>             return NO;
>         }
>         
>         NSURL *sourceURL = [NSURL fileURLWithPath:legacyPath 
> isDirectory:NO];
>         NSURL *destinationURL = [NSURL fileURLWithPath:originalPath 
> isDirectory:NO];
>
>         /*
>          Create the migration manager and perform the migration
>          */
>         
>         NSMigrationManager *migrationManager = [[NSMigrationManager alloc] 
> initWithSourceModel:sourceModel destinationModel:destinationModel];
>
>         ok = [migrationManager migrateStoreFromURL:sourceURL 
> type:NSSQLiteStoreType options:storeOptions 
> withMappingModel:mappingModel toDestinationURL:destinationURL 
> destinationType:NSSQLiteStoreType destinationOptions:storeOptions 
> error:error];
>         
>         [migrationManager release];
>         
>         if (!ok)
>         {
>             return NO;
>         }
>         
>         /*
>          Add the new store to the store coordinator and set the file URL
>          */
>         NSPersistentStoreCoordinator *psc = [[self managedObjectContext] 
> persistentStoreCoordinator];
>         
>         NSPersistentStore *destinationStore = [psc 
> addPersistentStoreWithType:NSSQLiteStoreType 
> configuration:configuration URL:destinationURL options:storeOptions 
> error:error];
>         
>         if (destinationStore == nil)
>         {
>             return NO;
>         }
>         
>         [self setFileURL:destinationURL];
>     }
>     
>
>
> Best,
> Pierre
>
>
> On Nov 6, 2007, at 8:50 PM, Adam Swift wrote:
>

>>
>> On Nov 6, 2007, at 7:05 AM, Pierre Bernard wrote:
>>

>>> Hi!
>>>
>>> I am seeing problems with Core Data automatic migration reading 
>>> files created using Tiger.
>>>
>>> The problem is triggered by the fact that Core Data determines the 
>>> source model to use by looking at the file's metadata. Legacy 
>>> files my lack the required NSStoreModelVersionHashes value. 
>>> Unfortunately Core Data currently (9A581) does not try to derive 
>>> that value from the file's actual content. It just fails to read 
>>> the file.
>>>
>>> My workaround is to let Core Data have a pass at the file. If it 
>>> fails, I check for the missing NSStoreModelVersionHashes value. If 
>>> it is indeed missing, I load appropriate metadata from a PLIST 
>>> file and amend the source file.
>>>
>>> Another solution would be to manually instantiate a migration 
>>> manager with the 1.0 model for source model. In the end I decided 
>>> against this solution which involves more code. The below solution 
>>> will work gracefully once the Core Data bug is fixed. I.e. the 
>>> workaround will never again be called.

>>
>> There's no way for Core Data to reliably determine the appropriate 
>> model for a Tiger store, consider that a store may have been 
>> created by an a run-time data model that was constructed from any 
>> number of entities from any number of models (or constructed 
>> programmatically entirely in-memory).
>>
>> When version hashes are not available, your best bet is to create a 
>> migration manager with the appropriate inputs.
>>
>> - adam
>>

>>> Best,
>>> Pierre Bernard
>>> Houdah Software s.à r.l.
>>>
>>> - (BOOL)configurePersistentStoreCoordinatorForURL:(NSURL *)url
>>>                                           ofType:(NSString *)fileType
>>>                               modelConfiguration:(NSString *)configuration
>>>                                      storeOptions:(NSDictionary *)storeOptions
>>>                                             error:(NSError **)error
>>> {
>>>     NSMutableDictionary *newStoreOptions;
>>>     
>>>     if (storeOptions == nil)
>>>     {
>>>         newStoreOptions = [NSMutableDictionary dictionary];
>>>     }
>>>     else
>>>     {
>>>         newStoreOptions = [[storeOptions mutableCopy] autorelease];
>>>     }
>>>     
>>>     [newStoreOptions setObject:[NSNumber numberWithBool:YES] 
>>> forKey:NSMigratePersistentStoresAutomaticallyOption];
>>>     
>>>     BOOL ok = [super configurePersistentStoreCoordinatorForURL:url
>>>                                                         ofType:fileType
>>>                                             modelConfiguration:configuration
>>>                                                   storeOptions:newStoreOptions
>>>                                                          error:error];
>>>     
>>>     if (!ok) {
>>>         /*
>>>          Legacy files created under Tiger may lack necessary metadata.
>>>          Add the metadata matching 1.0 files and retry.
>>>          */
>>>         NSDictionary *sourceMetadata = [NSPersistentStoreCoordinator 
>>> metadataForPersistentStoreOfType:NSSQLiteStoreType URL:url 
>>> error:error];
>>>         id versionHashes = [sourceMetadata 
>>> objectForKey:@"NSStoreModelVersionHashes"];
>>>         
>>>         if (versionHashes == nil) {
>>>             NSString *legacyMetadataPath = [[NSBundle mainBundle] 
>>> pathForResource:@"LegacyMetadata" ofType:@"plist"];
>>>             NSMutableDictionary *metadata = [NSMutableDictionary 
>>> dictionaryWithContentsOfFile:legacyMetadataPath];
>>>             
>>>             [metadata addEntriesFromDictionary:sourceMetadata];
>>>             
>>>             if (![NSPersistentStoreCoordinator setMetadata:metadata 
>>> forPersistentStoreOfType:NSSQLiteStoreType URL:url error:error]) {
>>>                 return NO;
>>>             }    
>>>             
>>>             *error = nil;
>>>             ok = [super configurePersistentStoreCoordinatorForURL:url
>>>                                                           ofType:fileType
>>>                                               modelConfiguration:configuration
>>>                                                      storeOptions:newStoreOptions
>>>                                                             error:error];
>>>         }
>>>     }
>>>     
>>>     if (ok)
>>>     {    
>>>         /*
>>>          If all went well, update the metadata
>>>          */
>>>         
>>>         NSURL *fileURL = [self fileURL];
>>>         
>>>         if (fileURL == nil) {
>>>             // File URL is null when writing new files
>>>             fileURL = url;
>>>         }
>>>         
>>>         NSPersistentStoreCoordinator *psc = [[self managedObjectContext] 
>>> persistentStoreCoordinator];
>>>         
>>>         id pStore = [psc persistentStoreForURL:fileURL];
>>>         
>>>         /*
>>>          configurePersistentStoreCoordinatorForURL is called when 
>>> document reopened
>>>          Check for existing metadata to avoid overwriting unnecessarily
>>>          */
>>>         id existingMetadata = [[psc metadataForPersistentStore:pStore] 
>>> objectForKey:(NSString *)kMDItemKeywords];
>>>         
>>>         if (existingMetadata == nil)
>>>         {
>>>             if (![self setMetadataForStoreAtURL:fileURL])
>>>             {
>>>                 return NO;
>>>             }
>>>         }
>>>     }
>>>     
>>>     return ok;
>>> }
>>>
>>> ---
>>> Pierre Bernard
>>> http://www.bernard-web.com/pierre
>>> http://www.houdah.com
>>>
>>>
>>>
>>> _______________________________________________
>>>
>>> Cocoa-dev mailing list (<email_removed>)
>>>
>>> Please do not post admin requests or moderator comments to the list.
>>> Contact the moderators at cocoa-dev-admins(at)lists.apple.com
>>>
>>> Help/Unsubscribe/Update your Subscription:
>>> http://lists.apple.com/mailman/options/cocoa-dev/<email_removed>
>>>
>>> This email sent to <email_removed>

>>

>
> ---
> Pierre Bernard
> http://www.bernard-web.com/pierre
> http://www.houdah.com
>
>
>

Related mailsAuthorDate
ml[Leopard, workaround] Core Data migration for Tiger files Pierre Bernard Nov 6, 16:05
mlRe: [Leopard, workaround] Core Data migration for Tiger files Adam Swift Nov 6, 20:50
mlRe: [Leopard, workaround] Core Data migration for Tiger files Pierre Bernard Nov 6, 21:17
mlRe: [Leopard, workaround] Core Data migration for Tiger files Adam Swift Nov 7, 00:09