swiftcore-datacore-data-migration

Swift Core Data Migration Crash : syntax error in "SELECT MAX(Z_PK) FROM (null)"


I'm trying out Core Data with a test project and I'm facing some issues with Core Data and migrations. During the lightweight automatic migration, a crash occurs with the following :

 [logging] near "null": syntax error in "SELECT MAX(Z_PK) FROM (null)"



[error] error: addPersistentStoreWithType:configuration:URL:options:error: returned error NSCocoaErrorDomain (134110)
CoreData: error: addPersistentStoreWithType:configuration:URL:options:error: returned error NSCocoaErrorDomain (134110)
CoreData: annotation: userInfo:
CoreData: annotation:   sourceURL : file:///var/mobile/Containers/Data/Application/D712273A-6E49-4B19-9919-3E55F6A5A37D/Library/Application%20Support/TestApp.sqlite
CoreData: annotation:   reason : Cannot migrate store in-place: near "null": syntax error
CoreData: annotation:   destinationURL : file:///var/mobile/Containers/Data/Application/D712273A-6E49-4B19-9919-3E55F6A5A37D/Library/Application%20Support/TestApp.sqlite
CoreData: annotation:   NSUnderlyingError : Error Domain=NSCocoaErrorDomain Code=134110 "An error occurred during persistent store migration." UserInfo={reason=near "null": syntax error, NSSQLiteErrorDomain=1, NSUnderlyingException=near "null": syntax error}
CoreData: annotation: storeType: SQLite
CoreData: annotation: configuration: (null)
CoreData: annotation: URL: file:///var/mobile/Containers/Data/Application/D712273A-6E49-4B19-9919-3E55F6A5A37D/Library/Application%20Support/TestApp.sqlite
CoreData: annotation: options:
CoreData: annotation:   NSInferMappingModelAutomaticallyOption : 1
CoreData: annotation:   NSMigratePersistentStoresAutomaticallyOption : 1
CoreData: annotation:   NSPersistentStoreFileProtectionKey : NSFileProtectionComplete
CoreData: annotation: <NSPersistentStoreCoordinator: 0x281cf1500>: Attempting recovery from error encountered during addPersistentStore: Error Domain=NSCocoaErrorDomain Code=134110 "An error occurred during persistent store migration."

Here is the code of the PersistenceCointainer used to load database in the SwiftUI App :

struct PersistenceController {
    static let shared = PersistenceController()

    static var preview: PersistenceController = {
        let result = PersistenceController(inMemory: true)
        let viewContext = result.container.viewContext
        for _ in 0..<10 {
            let newItem = Relation(context: viewContext)
            newItem.date = Date()
        }
        do {
            try viewContext.save()
        } catch {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
        return result
    }()

    var container: NSPersistentContainer

    init(inMemory: Bool = false) {
        container = NSPersistentContainer(name: "TestApp")
        
        if inMemory {
            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
        }
        
        if let storeDirectory = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first {
                let sqliteURL = storeDirectory.appendingPathComponent("TestApp.sqlite")
                
                //Set Protection for Core data sql file
                let description = NSPersistentStoreDescription(url: sqliteURL)
                description.shouldInferMappingModelAutomatically = true
                description.shouldMigrateStoreAutomatically = true
                description.setOption(FileProtectionType.complete as NSObject, forKey: NSPersistentStoreFileProtectionKey)
                container.persistentStoreDescriptions = [description]
            }
        
       
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

                /*
                 Typical reasons for an error here include:
                 * The parent directory does not exist, cannot be created, or disallows writing.
                 * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                 * The device is out of space.
                 * The store could not be migrated to the current model version.
                 Check the error message to determine what the actual problem was.
                 */
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        container.viewContext.automaticallyMergesChangesFromParent = true
        
    }
    
    
}

Here is more about the model I'm trying to change. I have a simple model called "Partner" :

enter image description here

enter image description here

I can show more details of the relations if necessary.

I'm simply adding a new String optional attribute, so I thought lightweight migration would be easy.

I have created a lot of test data to try some features and I'd enjoy avoiding to drop my database ^^ And since it's a test project I'd like to understand where the issue comes from to avoir repeating it.

Can anybody help understand where does this "SELECT MAX(Z_PK) FROM (null)" comes from and how I could fix it ?


Solution

  • Ok so I tried to dig inside the database and didn't find any strange-looking data.

    I juste created a Mapping Model from the old to the new Partner Model, and specified that the new field should take the "default" value, and the migrations worked : so I guess I'll never know what was the real reason for this bug :)

    My advice for anyone that would face this bug : try creating a Mapping Model (steps 7/8 in this tutorial)