Core Data store corruption - iphone

A handful of customers for my iPhone app are experiencing Core Data store corruption (I assume so, since the error is "Failed to save to data store: Operation could not be completed. (Cocoa error 259.)")
Has anyone else experienced this kind of store corruption? I am worried since I aim to soon push an update which performs a schema migration, and I am worried that this will expose even more problems.
I had assumed that the Core Data/SQLlite APIs use atomic operations and are immune to corruption except if the underlying filesystem experiences corruption.
Is there a way to reduce/prevent corruption, and a way to reproduce the corruption so I can test this (I have been unsuccessful thus far).
Edit:
Also getting this error: "The database at /var/mobile/Applications//Documents/foo.sqlite is corrupted. SQLite error code 11, database disk image is malformed."

It happens to me when I manually overwrote my Base.sqlite without deleting Base.sqlite-wal and Base.sqlite-shm. Indeed, these files are new SQLite 3.7 features, maybe added in iOS 7.
To resolve the problem, I deleted Base.sqlite-* and sqlite regenerated them from my new base version.

Also attempt to replicate the error by filling the drive on the Device -- you should receive a disk full error instead of the database corrupting, but it may be possible to get a corrupted database in this manner.

The error you're getting is defined in Foundation.h
NSFileReadCorruptFileError = 259, // Read error (file corrupt, bad format, etc)
I've never encountered it with an actual store but I have hit something similar with bad permissions (on the Mac.) I haven't seen anyone mention a similar error online either. The error prevention systems in Core Data are fairly robust.
I would guess that the easiest way to create this would be send the persistent store to look at the wrong file such as accidentally targeting it at a text file. If it expects an SQL store but finds something else it will complain that the file is corrupt. That's just a shot in the dark.
Edit
This will be hard to track down because errors like this are so rare in Core Data that there aren't any tools to assist finding the problem.
I would recommend:
Checking upstream of where the error is coming from code. Perhaps something is throwing the store off or is causing it to look in another place.
Check anywhere you might do something non-standard. For example, if you generate your own entity map in code, its easy to throw it off if your not careful.

For clarity, using Xcode 7.2.1, SQLite data store, Core Data object graph, for a prototype app.
My problem was detailed by the Xcode terminal as:
CoreData: error: (11) Fatal error. The database at
/Users/etc/Library/Developer/CoreSimulator/Devices/etc/data/Containers/Data/Application/etc/Library/Application Support/com.etc.etc/etc.sqlite is corrupted.
SQLite error code:11, 'database disk image is malformed'.
Effectively my app was able to load and read the SQLite data, but was unable to save.
This answer by SO user software evolved made sense to me. While using Simulator I was fairly certain I had interrupted a save operation on a managed object context with private queue concurrency type NSPrivateQueueConcurrencyType.
Further investigation (using SQLiteManager) revealed the SQLite table I had been saving to at the time was the cause of this problem.
I could have easily deleted the app (no public release yet) however I wanted to understand at least how to repair this problem.
Notes from this experience:
In the app delegate under the UIApplicationDelegate Protocol - (void)applicationWillResignActive:(UIApplication *)application, if your managed object context hasChanges be sure to include a database save method;
If using more than one queue, use the Home button to trigger the delegate method in item 1 before clicking the stop button in Xcode;
Repair the damaged database file - I developed a solution detailed below from the answer outlined on this webpage Fixing the SQLite error “The database disk image is malformed”.
SQLite Database File Repair Method:
Open terminal [terminal/sqlite commands];
Navigate to the appropriate file location [cd];
For backup purposes, make a copy of the 'malformed' database file (e.g.dbMalFormedBU.sqlite) (that can be deleted later if the repair is successful) [cp];
To be certain, delete the dbMalFormed.sqlite-shm and dbMalFormed.sqlite-wal files [rm];
Open your 'malformed' database file [sqlite3 dbMalFormed.sqlite];
Clone your database file [.clone dbMalFormedNew.sqlite];
Exit SQLite3 [.exit];
Delete the old 'malformed' database file [rm dbMalFormed.sqlite);
Rename the new database file to the name used previously [mv dbMalFormedNew.sqlite dbMalFormed.sqlite].

I experienced that error when trying to get the persistent store coordinator.
Multi-threading was the problem in my case. Fixed it wrapping the whole method with a #synchronized(self) {} block.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
#synchronized(self) {
// Quickly return persistent store coordinator if available
if (__persistentStoreCoordinator != nil) {
return __persistentStoreCoordinator;
}
// Persistent store coordination initialization, to be performed once
// ...
}
return _persistentStoreCoordinator;
}

Are you ever interacting with the database using the sqlite API? Or have you used any non-Apple tools to create your seed database?

I experienced "Core Data store corruption" when I was preloading the database with a separated thread(not main thread) and it has been really difficult to find this bug since only a few of my customers will crash their app because of this.

I recently ran across this problem. In my case I was performing a search and iterating over the objects to transform the data to XML and KML. I would then spawn the email handler and attach the files. Then I would update a field in the objects and finally save to the backing store (SQL Lite). The worked fine in 3.x. In 4.x it broke.
It was stupid on my part to do all the email handling before altering and saving the DB. Moving all non essential code to the point after the save cleared up this problem.
This problem would totally corrupt the SQL DB. In my case the error is:
File at path does not appear to be a SQLite database

Related

How to completely reset CoreData for an Xcode project?

My macOS application is a mess right now. I made some big changes to my Core Data model, and now, just launching the app causes the app and Xcode to crash. The app's crash log reveals the following reason for the crash:
Fatal error: Unresolved error Error Domain=NSCocoaErrorDomain Code=134140 "Persistent store migration failed, missing mapping model."
I don't want to create a mapping model. I don't care about the old CoreData model. I want all that data to go away. I have tried the following things:
Deleting Derived Data.
Deleting the ~/Library/Application Support/APP_NAME/ folder.
Using NSBatchDeleteRequest to delete all the entities.
I want to start fresh (without creating a completely new project). What do I need to do to start all over again with a fresh Core Data dataset ?
Search in the Finder for yourAppName.sqlite. I found one in
~/Library/Containers/yourcompany.yourAppName/Data/Library/Application Support/yourAppName/
Well, like #Willeke said, it seems to be as simple as finding the sqlite file and deleting it. That gets you a complete purge/reset without rebuilding the app. Still a pain though if you just want to tweak the structure a bit without losing the accumulated data. So for that scenario, maybe develop an export function first, dump the data, delete the sqlite file, then re-import the data.
Well if you are using the emulator, simply delete all data on it like so:

Accessing a CoreData entity between app versions, etc

I inherited a project that uses CoreData for a certain type of data storage. I want to remove CoreData from the app and convert the data into a different format...
Over the course of developing other features, I had removed the CoreData framework from the app and the model declarations in such, not expecting to need to convert the old data.
But now I do need to convert the old data. So the first steps I took were to go back into source control, dig out the xcdatamodel files from the old version and plug them back into the app.
Then I went to an older version of the app, installed in on a device, created some data...
Then when I go back to the newer version of the app with CoreData "re-installed", I get a crash claiming that the data entity in question cannot be found in the managed object context. I triple checked spelling and syntax and it checks out.
I feel like maybe I'm missing some vital piece of configuration to make it recognize the xcdatamodel file that declare the entity.
Any insight would be helpful.
What the core data schema versioned? Are you sure you have the right version? One thing you could try if all else fails is just to open the sqlite database file that core data uses for it's backing store and write your own migration routine.
Did you add new entity after 'that' old version? If yes then you need to write migration code for new version.
I made a syntactical flaw that was causing the full context to not be loaded properly... when I changed that then everything loaded fine.
Sometimes these frameworks give the most misleading errors...

sqlite database disk image malformed on iPhone SDK

I'm having an issue with a new application on the iPhone SDK using SQLite as the DB backend.
Occasionally, my app will stop loading data to my UITableViews and after downloading the device DB via the Organizer I can access the SQLite DB via the command line. I can query certain tables fine but not others without getting an "SQL error: database disk image is malformed" error. See a sqlite session below:
SQLite version 3.6.17
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> select * from user;
1|cpjolicoeur#gmail.com|cpjolicoeur||4d055e38bb1d3758|image/gif|cartoonme_avatar.gif||Craig|Jolicoeur|1|1
sqlite> select * from item;
SQL error: database disk image is malformed
sqlite>
In this example my user table works fine but my item table is malformed, which corresponds with what I am seeing in my app where the items dont load. The app doesnt crash, the data just doesnt load because of this malformed error.
Any ideas why this is happening? My only thought is that maybe the DB is being corrupted because I am writing to the SQLite DB via a background thread within the app. I download data from a webserver via an NSOperationQueue in a background thread and update the SQLite DB with the data downloaded. Would writing to the DB in a background thread (while potentially reading from the main thread) corrupt the DB, or is it something else?
You have to be very careful about background threads accessing the database while debugging! This is because when the debugger halts processing (such as at a breakpoint) all threads are paused, including threads that may be in the middle of a database call, somewhere in between a database "open" and a database "close" call.
If you are halted at a breakpoint, and click the stop sign in Xcode, your app exits immediately. This very often causes errors such as the one you saw, or the "corrupted database" error.
There really isn't any solution (because there is no way to modify the behavior of "stop tasks", but I have evolved some techniques to mitigate it:
1. Add code to detect the app entering the background and have your db operations gracefully stop.
2. Never use the stop sign to halt processing while debugging. Instead, when done with a breakpoint then "continue", hit the home button on the simulator or device (which should trigger the code you added in step 1), wait for the app to background, THEN you can stop the run.
In my case this had to do with iOS 7: On iOS 7 Core Data now uses the SQLite WAL journaling mode which writes data to a .db-wal file instead of directly to the .db file. In my app, I would copy a prepared .db file into Library/Application Support during an app update. The problem was that an old .db-wal file was still in that directory and I only replaced the .db file. That way, I ended up with a .db file that was out of sync with the old .db-wal file.
There are two solutions to this problem:
Make sure the .db, .db-wal and .db-shm files are deleted before you copy your new .db file into place.
Go back to the old pre-iOS 7 behavior like so: https://stackoverflow.com/a/18870738/171933
Depends on how SQLite is compiled, it may or may not be thread-safe. If you're using the built-in one, it may not have the compile-time options you're looking for.
For our app, we had to roll our own SQLite to add full text search. Take a look at this page.

How to programmatically fill a database

I currently have an iPhone app that reads data from an external XML file at start-up, and then writes this data to the database (it only reads/writes data that the user's app has not seen before, though)
My concern is that there is going to be a back catalogue of data of several years, and that the first time the user runs the app it will have to read this in and be atrociously slow.
Our proposed solution is to include this data "pre-built" into the applications database, so that it doesn't have to load in the archival data on first-load - that is already in the app when they purchase it.
My question is whether there is a way to automatically populate this data with data from, say, an XML file or something. The database is in SQLite. I would populate it by hand, but obviously this will take a very long time, so I was just wondering if anybody had a more...programmatic solution...
I'm going to flesh out Jason's answer, I've marked my post as a community wiki so I shouldn't get any points for this.
He's not talking about a dummy app - write the app as you normally would, but check to see if the database exists in your main bundle before you call the code that populates the plist. You run that in the simulator, pull out the generated sqllite database, and add it to your project - if you only need to read from it, you can read it from the main bundle directory. If you need to do further writes then copy it into the writable documents area, and use it from there. So basically for the main user, the code to populate the DB would never be called...
The only downside is you also end up including the plist files you are reading from, even though you only need the database. You could make a different build target that was a copy of the main one with the only difference being that it held the plist files, while the main target you built for the app store did not.
Not to take Jason's answering thunder, I can't comment yet so it has to be here.
The nice thing is that you can access the filesystem of the simulator right on your Mac. I am away from mine at the moment or I could tell you exactly where to look, but I just find it by putting the name of the db file into searchlight and just running with that.
You also do not need to wright any code to populate the db since you can use the command line tool to do the initial setup if that is more convenient.
You will need to copy it over though since resources are stored in the read only signed portion of the app bundle.
I had the same problem of you using sqlite, on massive insert it's really slow. So the best way it's provide directly a filled sqlite database.
You have another way, instead of INSERT INTO, to populate a sqlite db. You can produce a csv file for each table and load into the tables using your computer and the sqlite shell:
Just 2 simple commands:
.separator SEPARATOR
.import FILE TABLE
Example:
adslol:~ user$ sqlite3
SQLite version 3.6.12
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .separator ;
sqlite> .import myData.csv nameOfMyTable
sqlite> .quit
I hope it's what you was looking for :)
If you need a good client for sqlite3 try SQLite Manager, it's a Firefox add-ons.

Updating CoreData xcdatamodel file troubles - attribute type change

I noticed several questions related to this topic go unanswered. Is this such a gray area that nobody really understands it?
Here is my problem:
I am a midway in the development of my app and the app has never been used ouside of my iphone simulator.One of the attributes in my core data structure requires a type change.Since my app has never been used outside of my iPhone Simulator, I first deleted the sqlite file. Doubling the effort of this step, I also went into iPhone Simulator menu and selected "Reset Content and Settings...".
Than, I edited the xcdatamodel file and changed the type of my attribute. I saved the file and exited. Without any other changes, I compiled. I expected it to fail because of my type change. It did not! After this, I assigned a value with new type to my attribute and it fails to compile?!
Is there something else that I need to do for the change to take an effect?
I would really, really appreciate an answer to my question.
Thank you!
Core Data sometimes acts weird until you do Build -> Clean to build from scratch.
When you change the model and there is no sqlite file then Core Data will just create one off of the current model, so your first instance makes perfect sense.
In your second instance, if you did not delete the sqlite file (or reset the sim) between those two iterations you would get an error because the sqlite file already exists from the last run and it no longer matches the model.
Whenever you change the model you need to either version it or delete the sqlite file. Otherwise they will not match and produce an error.
If that is not the issue then it would be very helpful if you gave the details of the error you are seeing.
update
I'd still like to know the right way of dealing with the changes in core data in early development stages when there should not be a need for the migration.
The right way is to delete the application / reset the simulator and start with a fresh sqlite file. There is no other option other than migration and as you surmised, that is incorrect during development.