Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Data SQLite store becomes readonly after update

Tags:

ios

core-data

I have had reports of users being unable to use an iOS app after updating via the App Store because the SQLite database in use by Core Data apparently becomes readonly. This occurs with a read/write persistent store that is kept in the Documents folder of the app bundle.

The persistent store is created from scratch for each user the first time they log into the app. Its contents are maintained with a custom progressive managed object model migration. As it happens, there was no migration to perform for the most recent release, and thus the persistent store should be ready to open as soon as the app is launched.

The error that we received from the users is the same, and it happens early in the process launch after the upgrade completes. The userInfo of the NSError object is what we captured in this case:

NSSQLiteErrorDomain = 264;
NSUnderlyingException = "error during prepareSQL for SQL string 'SELECT Z_VERSION, Z_UUID, Z_PLIST FROM Z_METADATA' : attempt to write a readonly database";

Due to some shortcomings in what we are logging and how we are managing the log files, I do not know precisely when this happens in the process of opening the persistent store. We are using UIManagedDocument but not sharing any documents via iCloud. I assume that the error occurs while opening an existing document package, but even that is a guess. (Because of this, I made improvements to the logging for future releases.)

The closest I have come to reproducing the error is to use the file permissions 0444 on the persistentStore-shm file in the document package of an iOS simulator app installation and then try the SELECT statement using the sqlite3 command line interface. The error in that case is SQLITE_CANTOPEN rather than SQLITE_READONLY, so I am probably not on the right track with a theory about file permissions. From what I can tell, UIManagedDocument automatically fixes file permissions anyway.

What I am wondering is if anyone has experienced behavior similar to this? If so, is there any way to recover so that users do not have to go through the process of recreating the local data store?

like image 911
Patrick Avatar asked Jun 04 '14 16:06

Patrick


2 Answers

Add your own descriptor to the options to (re)enable history tracking...

Do this before loading the store as part of the container set up:

let container = NSPersistentContainer(name: "YourDataStoreIdentifier" )
        
let description = container.persistentStoreDescriptions.first
// This allows a 'non-iCloud' sycning 
// container to keep track of changes 
// as if it was an iCloud syncing container

description?.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
        
container.loadPersistentStores(...
like image 62
ChrisH Avatar answered Nov 18 '22 10:11

ChrisH


I ran in to this and it was rather puzzling. It turned out that the -shm file, the write-ahead log's index, had been cleared and needed to be rebuilt. This appears to happen automatically when loading the persistent store into an NSPersistentStoreCoordinator.

If you call +[NSPersistentStore metadataForPersistentStoreWithURL:error:] before doing that, which I was doing, you'll get the error you reported and the return value will be nil.

So the solution that worked for me is to attempt to retrieve the metadata and, if that fails with an error, to load the persistent store into an NSPersistentStoreCoordinator to rebuild the -shm. After doing that, calling metadataForPersistentStoreWithURL:error: will return the metadata without hitting the readonly error.

like image 45
mwhite Avatar answered Nov 18 '22 08:11

mwhite