Skip to content

Commit 76ec072

Browse files
authored
Fix 'NULL DEFAULT _' migrations. (#380)
* Fix 'NULL DEFAULT _' migrations. * clean up * Add test for when old device makes change and syncs to new device. * fixes * clean up * filter columnNames by allColumnNames * clean up
1 parent 57274d5 commit 76ec072

11 files changed

Lines changed: 395 additions & 129 deletions

Sources/SQLiteData/CloudKit/CloudKit+StructuredQueries.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@
146146

147147
@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *)
148148
extension CKRecord {
149+
func hasSet(key: String) -> Bool {
150+
self.encryptedValues["\(CKRecord.userModificationTimeKey)_\(key)"] != nil
151+
}
152+
149153
@discardableResult
150154
package func setValue(
151155
_ newValue: some CKRecordValueProtocol & Equatable,
@@ -233,6 +237,9 @@
233237
encryptedValues[at: key] = userModificationTime
234238
self.userModificationTime = userModificationTime
235239
return true
240+
} else if !hasSet(key: key) {
241+
encryptedValues[at: key] = userModificationTime
242+
self.userModificationTime = userModificationTime
236243
}
237244
return false
238245
}

Sources/SQLiteData/CloudKit/SyncEngine.swift

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2003,7 +2003,9 @@
20032003
) async throws -> QueryFragment {
20042004
let nonPrimaryKeyChangedColumns =
20052005
changedColumnNames
2006-
.filter { $0 != T.primaryKey.name }
2006+
.filter {
2007+
$0 != T.primaryKey.name && record.hasSet(key: $0)
2008+
}
20072009
guard
20082010
!nonPrimaryKeyChangedColumns.isEmpty
20092011
else {
@@ -2435,13 +2437,19 @@
24352437
record: CKRecord,
24362438
columnNames: some Collection<String>
24372439
) -> QueryFragment {
2438-
let allColumnNames = T.TableColumns.writableColumns.map(\.name)
2440+
let setColumnNames = T.TableColumns.writableColumns.map(\.name)
2441+
.filter { record.hasSet(key: $0) }
2442+
guard !setColumnNames.isEmpty
2443+
else {
2444+
return ""
2445+
}
2446+
let columnNames = columnNames.filter { setColumnNames.contains($0) }
24392447
let hasNonPrimaryKeyColumns = columnNames.contains { $0 != T.primaryKey.name }
24402448
var query: QueryFragment = "INSERT INTO \(T.self) ("
2441-
query.append(allColumnNames.map { "\(quote: $0)" }.joined(separator: ", "))
2449+
query.append(setColumnNames.map { "\(quote: $0)" }.joined(separator: ", "))
24422450
query.append(") VALUES (")
24432451
query.append(
2444-
allColumnNames
2452+
setColumnNames
24452453
.map { columnName in
24462454
if let asset = record[columnName] as? CKAsset {
24472455
@Dependency(\.dataManager) var dataManager

Tests/SQLiteDataTests/CloudKitTests/CloudKitTests.swift

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
tableName: "remindersLists",
2525
schema: """
2626
CREATE TABLE "remindersLists" (
27-
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
27+
"id" INT PRIMARY KEY NOT NULL,
2828
"title" TEXT NOT NULL ON CONFLICT REPLACE DEFAULT ''
2929
) STRICT
3030
""",
@@ -34,7 +34,7 @@
3434
isPrimaryKey: true,
3535
name: "id",
3636
isNotNull: true,
37-
type: "INTEGER"
37+
type: "INT"
3838
),
3939
[1]: TableInfo(
4040
defaultValue: "\'\'",
@@ -101,7 +101,7 @@
101101
tableName: "reminders",
102102
schema: """
103103
CREATE TABLE "reminders" (
104-
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
104+
"id" INT PRIMARY KEY NOT NULL,
105105
"dueDate" TEXT,
106106
"isCompleted" INTEGER NOT NULL ON CONFLICT REPLACE DEFAULT 0,
107107
"priority" INTEGER,
@@ -124,7 +124,7 @@
124124
isPrimaryKey: true,
125125
name: "id",
126126
isNotNull: true,
127-
type: "INTEGER"
127+
type: "INT"
128128
),
129129
[2]: TableInfo(
130130
defaultValue: "0",
@@ -177,7 +177,7 @@
177177
tableName: "reminderTags",
178178
schema: """
179179
CREATE TABLE "reminderTags" (
180-
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
180+
"id" INT PRIMARY KEY NOT NULL,
181181
"reminderID" INTEGER NOT NULL REFERENCES "reminders"("id") ON DELETE CASCADE,
182182
"tagID" TEXT NOT NULL REFERENCES "tags"("title") ON DELETE CASCADE ON UPDATE CASCADE
183183
) STRICT
@@ -188,7 +188,7 @@
188188
isPrimaryKey: true,
189189
name: "id",
190190
isNotNull: true,
191-
type: "INTEGER"
191+
type: "INT"
192192
),
193193
[1]: TableInfo(
194194
defaultValue: nil,
@@ -210,7 +210,7 @@
210210
tableName: "parents",
211211
schema: """
212212
CREATE TABLE "parents"(
213-
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
213+
"id" INT PRIMARY KEY NOT NULL
214214
) STRICT
215215
""",
216216
tableInfo: [
@@ -219,15 +219,15 @@
219219
isPrimaryKey: true,
220220
name: "id",
221221
isNotNull: true,
222-
type: "INTEGER"
222+
type: "INT"
223223
)
224224
]
225225
),
226226
[7]: RecordType(
227227
tableName: "childWithOnDeleteSetNulls",
228228
schema: """
229229
CREATE TABLE "childWithOnDeleteSetNulls"(
230-
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
230+
"id" INT PRIMARY KEY NOT NULL,
231231
"parentID" INTEGER REFERENCES "parents"("id") ON DELETE SET NULL ON UPDATE SET NULL
232232
) STRICT
233233
""",
@@ -237,7 +237,7 @@
237237
isPrimaryKey: true,
238238
name: "id",
239239
isNotNull: true,
240-
type: "INTEGER"
240+
type: "INT"
241241
),
242242
[1]: TableInfo(
243243
defaultValue: nil,
@@ -252,7 +252,7 @@
252252
tableName: "childWithOnDeleteSetDefaults",
253253
schema: """
254254
CREATE TABLE "childWithOnDeleteSetDefaults"(
255-
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
255+
"id" INT PRIMARY KEY NOT NULL,
256256
"parentID" INTEGER NOT NULL DEFAULT 0
257257
REFERENCES "parents"("id") ON DELETE SET DEFAULT ON UPDATE SET DEFAULT
258258
) STRICT
@@ -263,7 +263,7 @@
263263
isPrimaryKey: true,
264264
name: "id",
265265
isNotNull: true,
266-
type: "INTEGER"
266+
type: "INT"
267267
),
268268
[1]: TableInfo(
269269
defaultValue: "0",
@@ -278,7 +278,7 @@
278278
tableName: "modelAs",
279279
schema: """
280280
CREATE TABLE "modelAs" (
281-
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
281+
"id" INT PRIMARY KEY NOT NULL,
282282
"count" INTEGER NOT NULL ON CONFLICT REPLACE DEFAULT 0,
283283
"isEven" INTEGER GENERATED ALWAYS AS ("count" % 2 == 0) VIRTUAL
284284
)
@@ -296,15 +296,15 @@
296296
isPrimaryKey: true,
297297
name: "id",
298298
isNotNull: true,
299-
type: "INTEGER"
299+
type: "INT"
300300
)
301301
]
302302
),
303303
[10]: RecordType(
304304
tableName: "modelBs",
305305
schema: """
306306
CREATE TABLE "modelBs" (
307-
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
307+
"id" INT PRIMARY KEY NOT NULL,
308308
"isOn" INTEGER NOT NULL ON CONFLICT REPLACE DEFAULT 0,
309309
"modelAID" INTEGER NOT NULL REFERENCES "modelAs"("id") ON DELETE CASCADE
310310
)
@@ -315,7 +315,7 @@
315315
isPrimaryKey: true,
316316
name: "id",
317317
isNotNull: true,
318-
type: "INTEGER"
318+
type: "INT"
319319
),
320320
[1]: TableInfo(
321321
defaultValue: "0",
@@ -337,7 +337,7 @@
337337
tableName: "modelCs",
338338
schema: """
339339
CREATE TABLE "modelCs" (
340-
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
340+
"id" INT PRIMARY KEY NOT NULL,
341341
"title" TEXT NOT NULL ON CONFLICT REPLACE DEFAULT '',
342342
"modelBID" INTEGER NOT NULL REFERENCES "modelBs"("id") ON DELETE CASCADE
343343
)
@@ -348,7 +348,7 @@
348348
isPrimaryKey: true,
349349
name: "id",
350350
isNotNull: true,
351-
type: "INTEGER"
351+
type: "INT"
352352
),
353353
[1]: TableInfo(
354354
defaultValue: nil,

Tests/SQLiteDataTests/CloudKitTests/FetchRecordZoneChangesTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,8 +404,8 @@
404404

405405
@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
406406
@Test func receiveRecord_SingleFieldPrimaryKey() async throws {
407-
let tagRecord = CKRecord(recordType: "tags", recordID: Tag.recordID(for: "weekend"))
408-
tagRecord.encryptedValues["title"] = "weekend"
407+
let tagRecord = CKRecord(recordType: Tag.tableName, recordID: Tag.recordID(for: "weekend"))
408+
tagRecord.setValue("weekend", forKey: "title", at: 0)
409409
try await syncEngine.modifyRecords(scope: .private, saving: [tagRecord]).notify()
410410

411411
try await userDatabase.read { db in

Tests/SQLiteDataTests/CloudKitTests/ForeignKeyConstraintTests.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,13 @@
119119
@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
120120
@Test func remoteCreatesRecordABC_localReceivesAC_remoteDeletesBC() async throws {
121121
let modelARecord = CKRecord(recordType: ModelA.tableName, recordID: ModelA.recordID(for: 1))
122+
modelARecord.setValue(1, forKey: "id", at: now)
122123
let modelBRecord = CKRecord(recordType: ModelB.tableName, recordID: ModelB.recordID(for: 1))
124+
modelBRecord.setValue(1, forKey: "id", at: now)
123125
modelBRecord.setValue(1, forKey: "modelAID", at: now)
124126
modelBRecord.parent = CKRecord.Reference(record: modelARecord, action: .none)
125127
let modelCRecord = CKRecord(recordType: ModelC.tableName, recordID: ModelC.recordID(for: 1))
128+
modelCRecord.setValue(1, forKey: "id", at: now)
126129
modelCRecord.setValue(1, forKey: "modelBID", at: now)
127130
modelCRecord.parent = CKRecord.Reference(record: modelBRecord, action: .none)
128131

@@ -205,7 +208,8 @@
205208
recordID: CKRecord.ID(1:modelAs/zone/__defaultOwner__),
206209
recordType: "modelAs",
207210
parent: nil,
208-
share: nil
211+
share: nil,
212+
id: 1
209213
)
210214
]
211215
),

0 commit comments

Comments
 (0)