diff --git a/Package.resolved b/Package.resolved index bc05ab9a..a5072755 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "5f7df83a450ddad2662622f41356bb1fb40d7774f6d1e85438ffac479b93045a", + "originHash" : "32f70dab99ea92a018a46453e9ed1e8bbf1e5d17b2993ba8abc63cae49896bbc", "pins" : [ { "identity" : "combine-schedulers", @@ -123,8 +123,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-structured-queries", "state" : { - "revision" : "1b653aba57486afef66d8aafdbe83246249118fd", - "version" : "0.19.0" + "revision" : "f576582c138311e442c564bd7a893e950765e46a", + "version" : "0.19.1" } }, { diff --git a/Package.swift b/Package.swift index 6c1e8b2a..c1d9d21d 100644 --- a/Package.swift +++ b/Package.swift @@ -35,7 +35,7 @@ let package = Package( .package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.18.4"), .package( url: "https://github.com/pointfreeco/swift-structured-queries", - from: "0.19.0", + from: "0.19.1", traits: [ .trait(name: "StructuredQueriesTagged", condition: .when(traits: ["SQLiteDataTagged"])) ] diff --git a/Package@swift-6.0.swift b/Package@swift-6.0.swift index 5d4e3302..7f09a24d 100644 --- a/Package@swift-6.0.swift +++ b/Package@swift-6.0.swift @@ -27,7 +27,7 @@ let package = Package( .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.9.0"), .package(url: "https://github.com/pointfreeco/swift-sharing", from: "2.3.0"), .package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.18.4"), - .package(url: "https://github.com/pointfreeco/swift-structured-queries", from: "0.19.0"), + .package(url: "https://github.com/pointfreeco/swift-structured-queries", from: "0.19.1"), .package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "1.5.0"), ], targets: [ diff --git a/Sources/SQLiteData/CloudKit/Internal/CloudKitFunctions.swift b/Sources/SQLiteData/CloudKit/Internal/CloudKitFunctions.swift index fb1fa47f..d7d6670a 100644 --- a/Sources/SQLiteData/CloudKit/Internal/CloudKitFunctions.swift +++ b/Sources/SQLiteData/CloudKit/Internal/CloudKitFunctions.swift @@ -10,7 +10,8 @@ @DatabaseFunction( "sqlitedata_icloud_hasPermission", - as: ((CKShare?.SystemFieldsRepresentation) -> Bool).self + as: ((CKShare?.SystemFieldsRepresentation) -> Bool).self, + isDeterministic: true ) func hasPermission(_ share: CKShare?) -> Bool { guard let share else { return true } diff --git a/Sources/SQLiteData/CloudKit/Internal/Triggers.swift b/Sources/SQLiteData/CloudKit/Internal/Triggers.swift index c2fbe727..c934286b 100644 --- a/Sources/SQLiteData/CloudKit/Internal/Triggers.swift +++ b/Sources/SQLiteData/CloudKit/Internal/Triggers.swift @@ -145,8 +145,14 @@ Values( syncEngine.$didUpdate( recordName: new.recordName, - record: new.lastKnownServerRecord - ?? rootServerRecord(recordName: new.recordName) + lastKnownServerRecord: new.lastKnownServerRecord + ?? rootServerRecord(recordName: new.recordName), + newParentLastKnownServerRecord: parentLastKnownServerRecordIfShared( + parentRecordPrimaryKey: new.parentRecordPrimaryKey, + parentRecordType: new.parentRecordType + ), + parentRecordPrimaryKey: new.parentRecordPrimaryKey, + parentRecordType: new.parentRecordType ) ) } when: { _ in @@ -165,8 +171,14 @@ Values( syncEngine.$didUpdate( recordName: new.recordName, - record: new.lastKnownServerRecord - ?? rootServerRecord(recordName: new.recordName) + lastKnownServerRecord: new.lastKnownServerRecord + ?? rootServerRecord(recordName: new.recordName), + newParentLastKnownServerRecord: parentLastKnownServerRecordIfShared( + parentRecordPrimaryKey: new.parentRecordPrimaryKey, + parentRecordType: new.parentRecordType + ), + parentRecordPrimaryKey: new.parentRecordPrimaryKey, + parentRecordType: new.parentRecordType ) ) } when: { old, new in @@ -281,6 +293,19 @@ } } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) + private func parentLastKnownServerRecordIfShared( + parentRecordPrimaryKey: some QueryExpression, + parentRecordType: some QueryExpression + ) -> some QueryExpression { + SyncMetadata + .select(\.lastKnownServerRecord) + .where { + $0.recordPrimaryKey.is(parentRecordPrimaryKey) + && $0.recordType.is(parentRecordType) + } + } + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) extension AncestorMetadata.Columns { init(_ metadata: SyncMetadata.TableColumns) { diff --git a/Sources/SQLiteData/CloudKit/SyncEngine.swift b/Sources/SQLiteData/CloudKit/SyncEngine.swift index 990e66f0..3ad8abf5 100644 --- a/Sources/SQLiteData/CloudKit/SyncEngine.swift +++ b/Sources/SQLiteData/CloudKit/SyncEngine.swift @@ -596,10 +596,37 @@ @DatabaseFunction( "sqlitedata_icloud_didUpdate", - as: ((String, CKRecord?.SystemFieldsRepresentation) -> Void).self + as: ( + (String, CKRecord?.SystemFieldsRepresentation, CKRecord?.SystemFieldsRepresentation, String?, String?) -> Void + ).self ) - func didUpdate(recordName: String, record: CKRecord?) { - let zoneID = record?.recordID.zoneID ?? defaultZone.zoneID + func didUpdate( + recordName: String, + lastKnownServerRecord: CKRecord?, + newParentLastKnownServerRecord: CKRecord?, + parentRecordPrimaryKey: String? = nil, + parentRecordType: String? = nil + ) throws { + let zoneID = lastKnownServerRecord?.recordID.zoneID ?? defaultZone.zoneID + let newZoneID = newParentLastKnownServerRecord?.recordID.zoneID + if let newZoneID, zoneID != newZoneID { + struct ZoneChangingError: Error, LocalizedError { + let recordName: String + let zoneID: CKRecordZone.ID + let newZoneID: CKRecordZone.ID + var errorDescription: String? { + """ + The record '\(recordName)' was moved from zone \ + '\(zoneID.zoneName)/\(zoneID.ownerName)' to \ + '\(newZoneID.zoneName)/\(newZoneID.ownerName)'. This is currently not supported in \ + SQLiteData. To work around, delete the record and then create a new record with its \ + new parent association. + """ + } + } + throw ZoneChangingError(recordName: recordName, zoneID: zoneID, newZoneID: newZoneID) + } + let change = CKSyncEngine.PendingRecordZoneChange.saveRecord( CKRecord.ID( recordName: recordName, @@ -1345,7 +1372,9 @@ let table = tablesByName[failedRecord.recordType], foreignKeysByTableName[table.tableName]?.count == 1, let foreignKey = foreignKeysByTableName[table.tableName]?.first - else { continue } + else { + continue + } func open(_: T.Type) async throws { try await userDatabase.write { db in try $_isSynchronizingChanges.withValue(false) { diff --git a/Sources/SQLiteData/CloudKit/SyncMetadata.swift b/Sources/SQLiteData/CloudKit/SyncMetadata.swift index 8fca929d..29a05cb7 100644 --- a/Sources/SQLiteData/CloudKit/SyncMetadata.swift +++ b/Sources/SQLiteData/CloudKit/SyncMetadata.swift @@ -66,7 +66,7 @@ @Column(generated: .virtual) public let hasLastKnownServerRecord: Bool - + /// Determines if the record associated with this metadata is currently shared in CloudKit. /// /// This can only return `true` for root records. For example, the metadata associated with a diff --git a/Tests/SQLiteDataTests/CloudKitTests/SharingTests.swift b/Tests/SQLiteDataTests/CloudKitTests/SharingTests.swift index 12ce91a5..00629259 100644 --- a/Tests/SQLiteDataTests/CloudKitTests/SharingTests.swift +++ b/Tests/SQLiteDataTests/CloudKitTests/SharingTests.swift @@ -355,6 +355,97 @@ } try await syncEngine.processPendingRecordZoneChanges(scope: .shared) + assertQuery(SyncMetadata.all, database: syncEngine.metadatabase) { + """ + ┌─────────────────────────────────────────────────────────────────────────────────────────┐ + │ SyncMetadata( │ + │ recordPrimaryKey: "1", │ + │ recordType: "modelAs", │ + │ recordName: "1:modelAs", │ + │ parentRecordPrimaryKey: nil, │ + │ parentRecordType: nil, │ + │ parentRecordName: nil, │ + │ lastKnownServerRecord: CKRecord( │ + │ recordID: CKRecord.ID(1:modelAs/external.zone/external.owner), │ + │ recordType: "modelAs", │ + │ parent: nil, │ + │ share: nil │ + │ ), │ + │ _lastKnownServerRecordAllFields: CKRecord( │ + │ recordID: CKRecord.ID(1:modelAs/external.zone/external.owner), │ + │ recordType: "modelAs", │ + │ parent: nil, │ + │ share: nil, │ + │ count: 0, │ + │ id: 1 │ + │ ), │ + │ share: nil, │ + │ _isDeleted: false, │ + │ hasLastKnownServerRecord: true, │ + │ isShared: false, │ + │ userModificationDate: Date(1970-01-01T00:00:00.000Z) │ + │ ) │ + ├─────────────────────────────────────────────────────────────────────────────────────────┤ + │ SyncMetadata( │ + │ recordPrimaryKey: "1", │ + │ recordType: "modelBs", │ + │ recordName: "1:modelBs", │ + │ parentRecordPrimaryKey: "1", │ + │ parentRecordType: "modelAs", │ + │ parentRecordName: "1:modelAs", │ + │ lastKnownServerRecord: CKRecord( │ + │ recordID: CKRecord.ID(1:modelBs/external.zone/external.owner), │ + │ recordType: "modelBs", │ + │ parent: CKReference(recordID: CKRecord.ID(1:modelAs/external.zone/external.owner)), │ + │ share: nil │ + │ ), │ + │ _lastKnownServerRecordAllFields: CKRecord( │ + │ recordID: CKRecord.ID(1:modelBs/external.zone/external.owner), │ + │ recordType: "modelBs", │ + │ parent: CKReference(recordID: CKRecord.ID(1:modelAs/external.zone/external.owner)), │ + │ share: nil, │ + │ id: 1, │ + │ isOn: 0, │ + │ modelAID: 1 │ + │ ), │ + │ share: nil, │ + │ _isDeleted: false, │ + │ hasLastKnownServerRecord: true, │ + │ isShared: false, │ + │ userModificationDate: Date(1970-01-01T00:01:00.000Z) │ + │ ) │ + ├─────────────────────────────────────────────────────────────────────────────────────────┤ + │ SyncMetadata( │ + │ recordPrimaryKey: "1", │ + │ recordType: "modelCs", │ + │ recordName: "1:modelCs", │ + │ parentRecordPrimaryKey: "1", │ + │ parentRecordType: "modelBs", │ + │ parentRecordName: "1:modelBs", │ + │ lastKnownServerRecord: CKRecord( │ + │ recordID: CKRecord.ID(1:modelCs/external.zone/external.owner), │ + │ recordType: "modelCs", │ + │ parent: CKReference(recordID: CKRecord.ID(1:modelBs/external.zone/external.owner)), │ + │ share: nil │ + │ ), │ + │ _lastKnownServerRecordAllFields: CKRecord( │ + │ recordID: CKRecord.ID(1:modelCs/external.zone/external.owner), │ + │ recordType: "modelCs", │ + │ parent: CKReference(recordID: CKRecord.ID(1:modelBs/external.zone/external.owner)), │ + │ share: nil, │ + │ id: 1, │ + │ modelBID: 1, │ + │ title: "" │ + │ ), │ + │ share: nil, │ + │ _isDeleted: false, │ + │ hasLastKnownServerRecord: true, │ + │ isShared: false, │ + │ userModificationDate: Date(1970-01-01T00:01:00.000Z) │ + │ ) │ + └─────────────────────────────────────────────────────────────────────────────────────────┘ + """ + } assertInlineSnapshot(of: container, as: .customDump) { """ MockCloudContainer( @@ -629,7 +720,9 @@ ) _ = try syncEngine.modifyRecords(scope: .shared, saving: [share, remindersListRecord]) let freshShare = try syncEngine.shared.database.record(for: share.recordID) as! CKShare - let freshRemindersListRecord = try syncEngine.shared.database.record(for: remindersListRecord.recordID) + let freshRemindersListRecord = try syncEngine.shared.database.record( + for: remindersListRecord.recordID + ) try await syncEngine .acceptShare( @@ -909,6 +1002,217 @@ """ } } + + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) + @Test func movesChildRecordFromPrivateParentToSharedParent() async throws { + try await userDatabase.userWrite { db in + try db.seed { + ModelA.Draft(id: 1, count: 42) + ModelB.Draft(id: 1, isOn: true, modelAID: 1) + } + } + try await syncEngine.processPendingRecordZoneChanges(scope: .private) + + let externalZone = CKRecordZone( + zoneID: CKRecordZone.ID( + zoneName: "external.zone", + ownerName: "external.owner" + ) + ) + try await syncEngine.modifyRecordZones(scope: .shared, saving: [externalZone]).notify() + + let modelARecord = CKRecord( + recordType: ModelA.tableName, + recordID: ModelA.recordID(for: 2, zoneID: externalZone.zoneID) + ) + modelARecord.setValue(2, forKey: "id", at: now) + modelARecord.setValue(1729, forKey: "count", at: now) + let share = CKShare( + rootRecord: modelARecord, + shareID: CKRecord.ID( + recordName: "share-\(modelARecord.recordID.recordName)", + zoneID: modelARecord.recordID.zoneID + ) + ) + _ = try syncEngine.modifyRecords(scope: .shared, saving: [share, modelARecord]) + let freshShare = try syncEngine.shared.database.record(for: share.recordID) as! CKShare + let freshModelARecord = try syncEngine.shared.database.record(for: modelARecord.recordID) + + try await syncEngine + .acceptShare( + metadata: ShareMetadata( + containerIdentifier: container.containerIdentifier!, + hierarchicalRootRecordID: freshModelARecord.recordID, + rootRecord: freshModelARecord, + share: freshShare + ) + ) + + let error = await #expect(throws: DatabaseError.self) { + try await self.userDatabase.userWrite { db in + try ModelB.find(1).update { $0.modelAID = 2 }.execute(db) + } + } + #expect(error?.message == """ + The record '1:modelBs' was moved from zone 'zone/__defaultOwner__' to \ + 'external.zone/external.owner'. This is currently not supported in SQLiteData. To work \ + around, delete the record and then create a new record with its new parent association. + """) + + assertQuery(ModelB.all, database: userDatabase.database) { + """ + ┌───────────────┐ + │ ModelB( │ + │ id: 1, │ + │ isOn: true, │ + │ modelAID: 1 │ + │ ) │ + └───────────────┘ + """ + } + assertQuery(SyncMetadata.all, database: syncEngine.metadatabase) { + """ + ┌──────────────────────────────────────────────────────────────────────────────────────────────┐ + │ SyncMetadata( │ + │ recordPrimaryKey: "1", │ + │ recordType: "modelAs", │ + │ recordName: "1:modelAs", │ + │ parentRecordPrimaryKey: nil, │ + │ parentRecordType: nil, │ + │ parentRecordName: nil, │ + │ lastKnownServerRecord: CKRecord( │ + │ recordID: CKRecord.ID(1:modelAs/zone/__defaultOwner__), │ + │ recordType: "modelAs", │ + │ parent: nil, │ + │ share: nil │ + │ ), │ + │ _lastKnownServerRecordAllFields: CKRecord( │ + │ recordID: CKRecord.ID(1:modelAs/zone/__defaultOwner__), │ + │ recordType: "modelAs", │ + │ parent: nil, │ + │ share: nil, │ + │ count: 42, │ + │ id: 1 │ + │ ), │ + │ share: nil, │ + │ _isDeleted: false, │ + │ hasLastKnownServerRecord: true, │ + │ isShared: false, │ + │ userModificationDate: Date(1970-01-01T00:00:00.000Z) │ + │ ) │ + ├──────────────────────────────────────────────────────────────────────────────────────────────┤ + │ SyncMetadata( │ + │ recordPrimaryKey: "1", │ + │ recordType: "modelBs", │ + │ recordName: "1:modelBs", │ + │ parentRecordPrimaryKey: "1", │ + │ parentRecordType: "modelAs", │ + │ parentRecordName: "1:modelAs", │ + │ lastKnownServerRecord: CKRecord( │ + │ recordID: CKRecord.ID(1:modelBs/zone/__defaultOwner__), │ + │ recordType: "modelBs", │ + │ parent: CKReference(recordID: CKRecord.ID(1:modelAs/zone/__defaultOwner__)), │ + │ share: nil │ + │ ), │ + │ _lastKnownServerRecordAllFields: CKRecord( │ + │ recordID: CKRecord.ID(1:modelBs/zone/__defaultOwner__), │ + │ recordType: "modelBs", │ + │ parent: CKReference(recordID: CKRecord.ID(1:modelAs/zone/__defaultOwner__)), │ + │ share: nil, │ + │ id: 1, │ + │ isOn: 1, │ + │ modelAID: 1 │ + │ ), │ + │ share: nil, │ + │ _isDeleted: false, │ + │ hasLastKnownServerRecord: true, │ + │ isShared: false, │ + │ userModificationDate: Date(1970-01-01T00:00:00.000Z) │ + │ ) │ + ├──────────────────────────────────────────────────────────────────────────────────────────────┤ + │ SyncMetadata( │ + │ recordPrimaryKey: "2", │ + │ recordType: "modelAs", │ + │ recordName: "2:modelAs", │ + │ parentRecordPrimaryKey: nil, │ + │ parentRecordType: nil, │ + │ parentRecordName: nil, │ + │ lastKnownServerRecord: CKRecord( │ + │ recordID: CKRecord.ID(2:modelAs/external.zone/external.owner), │ + │ recordType: "modelAs", │ + │ parent: nil, │ + │ share: CKReference(recordID: CKRecord.ID(share-2:modelAs/external.zone/external.owner)) │ + │ ), │ + │ _lastKnownServerRecordAllFields: CKRecord( │ + │ recordID: CKRecord.ID(2:modelAs/external.zone/external.owner), │ + │ recordType: "modelAs", │ + │ parent: nil, │ + │ share: CKReference(recordID: CKRecord.ID(share-2:modelAs/external.zone/external.owner)), │ + │ count: 1729, │ + │ id: 2 │ + │ ), │ + │ share: CKRecord( │ + │ recordID: CKRecord.ID(share-2:modelAs/external.zone/external.owner), │ + │ recordType: "cloudkit.share", │ + │ parent: nil, │ + │ share: nil │ + │ ), │ + │ _isDeleted: false, │ + │ hasLastKnownServerRecord: true, │ + │ isShared: true, │ + │ userModificationDate: Date(1970-01-01T00:00:00.000Z) │ + │ ) │ + └──────────────────────────────────────────────────────────────────────────────────────────────┘ + """ + } + assertInlineSnapshot(of: container, as: .customDump) { + """ + MockCloudContainer( + privateCloudDatabase: MockCloudDatabase( + databaseScope: .private, + storage: [ + [0]: CKRecord( + recordID: CKRecord.ID(1:modelAs/zone/__defaultOwner__), + recordType: "modelAs", + parent: nil, + share: nil, + count: 42, + id: 1 + ), + [1]: CKRecord( + recordID: CKRecord.ID(1:modelBs/zone/__defaultOwner__), + recordType: "modelBs", + parent: CKReference(recordID: CKRecord.ID(1:modelAs/zone/__defaultOwner__)), + share: nil, + id: 1, + isOn: 1, + modelAID: 1 + ) + ] + ), + sharedCloudDatabase: MockCloudDatabase( + databaseScope: .shared, + storage: [ + [0]: CKRecord( + recordID: CKRecord.ID(share-2:modelAs/external.zone/external.owner), + recordType: "cloudkit.share", + parent: nil, + share: nil + ), + [1]: CKRecord( + recordID: CKRecord.ID(2:modelAs/external.zone/external.owner), + recordType: "modelAs", + parent: nil, + share: CKReference(recordID: CKRecord.ID(share-2:modelAs/external.zone/external.owner)), + count: 1729, + id: 2 + ) + ] + ) + ) + """ + } + } } } #endif diff --git a/Tests/SQLiteDataTests/CloudKitTests/SyncEngineLifecycleTests.swift b/Tests/SQLiteDataTests/CloudKitTests/SyncEngineLifecycleTests.swift index 8ed166d6..60e3e0aa 100644 --- a/Tests/SQLiteDataTests/CloudKitTests/SyncEngineLifecycleTests.swift +++ b/Tests/SQLiteDataTests/CloudKitTests/SyncEngineLifecycleTests.swift @@ -266,6 +266,58 @@ } } + try await Task.sleep(for: .seconds(1)) + assertQuery(SyncMetadata.all, database: syncEngine.metadatabase) { + """ + ┌───────────────────────────────────────────────────────────────────────────┐ + │ SyncMetadata( │ + │ recordPrimaryKey: "1", │ + │ recordType: "remindersLists", │ + │ recordName: "1:remindersLists", │ + │ parentRecordPrimaryKey: nil, │ + │ parentRecordType: nil, │ + │ parentRecordName: nil, │ + │ lastKnownServerRecord: CKRecord( │ + │ recordID: CKRecord.ID(1:remindersLists/external.zone/external.owner), │ + │ recordType: "remindersLists", │ + │ parent: nil, │ + │ share: nil │ + │ ), │ + │ _lastKnownServerRecordAllFields: CKRecord( │ + │ recordID: CKRecord.ID(1:remindersLists/external.zone/external.owner), │ + │ recordType: "remindersLists", │ + │ parent: nil, │ + │ share: nil, │ + │ id: 1, │ + │ isCompleted: 0, │ + │ title: "Personal" │ + │ ), │ + │ share: nil, │ + │ _isDeleted: false, │ + │ hasLastKnownServerRecord: true, │ + │ isShared: false, │ + │ userModificationDate: Date(1970-01-01T00:00:00.000Z) │ + │ ) │ + ├───────────────────────────────────────────────────────────────────────────┤ + │ SyncMetadata( │ + │ recordPrimaryKey: "1", │ + │ recordType: "reminders", │ + │ recordName: "1:reminders", │ + │ parentRecordPrimaryKey: "1", │ + │ parentRecordType: "remindersLists", │ + │ parentRecordName: "1:remindersLists", │ + │ lastKnownServerRecord: nil, │ + │ _lastKnownServerRecordAllFields: nil, │ + │ share: nil, │ + │ _isDeleted: false, │ + │ hasLastKnownServerRecord: false, │ + │ isShared: false, │ + │ userModificationDate: Date(1970-01-01T00:01:00.000Z) │ + │ ) │ + └───────────────────────────────────────────────────────────────────────────┘ + """ + } + try await Task.sleep(for: .seconds(0.5)) try await syncEngine.start() try await syncEngine.processPendingRecordZoneChanges(scope: .shared) diff --git a/Tests/SQLiteDataTests/CloudKitTests/TriggerTests.swift b/Tests/SQLiteDataTests/CloudKitTests/TriggerTests.swift index 5c407e2a..9afac03a 100644 --- a/Tests/SQLiteDataTests/CloudKitTests/TriggerTests.swift +++ b/Tests/SQLiteDataTests/CloudKitTests/TriggerTests.swift @@ -57,7 +57,11 @@ SELECT "ancestorMetadatas"."lastKnownServerRecord" FROM "ancestorMetadatas" WHERE ("ancestorMetadatas"."parentRecordName" IS NULL) - ))); + )), ( + SELECT "sqlitedata_icloud_metadata"."lastKnownServerRecord" + FROM "sqlitedata_icloud_metadata" + WHERE (("sqlitedata_icloud_metadata"."recordPrimaryKey" IS "new"."parentRecordPrimaryKey") AND ("sqlitedata_icloud_metadata"."recordType" IS "new"."parentRecordType")) + ), "new"."parentRecordPrimaryKey", "new"."parentRecordType"); END """, [2]: """ @@ -79,7 +83,11 @@ SELECT "ancestorMetadatas"."lastKnownServerRecord" FROM "ancestorMetadatas" WHERE ("ancestorMetadatas"."parentRecordName" IS NULL) - ))); + )), ( + SELECT "sqlitedata_icloud_metadata"."lastKnownServerRecord" + FROM "sqlitedata_icloud_metadata" + WHERE (("sqlitedata_icloud_metadata"."recordPrimaryKey" IS "new"."parentRecordPrimaryKey") AND ("sqlitedata_icloud_metadata"."recordType" IS "new"."parentRecordType")) + ), "new"."parentRecordPrimaryKey", "new"."parentRecordType"); END """, [3]: """