Skip to content

Commit 196c33b

Browse files
authored
Merge pull request #1822 from groue/dev/issue-1820
Fix the save() method for record types with attached triggers
2 parents c5d02ea + 50a74f7 commit 196c33b

File tree

5 files changed

+91
-12
lines changed

5 files changed

+91
-12
lines changed

GRDB/Core/Database.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2018,6 +2018,34 @@ extension Database {
20182018
}
20192019
#endif
20202020

2021+
extension Database {
2022+
/// Returns the count of changes executed by one statement execution.
2023+
func countChanges<T>(_ count: inout Int, forTable tableName: String, updates: () throws -> T) throws -> T {
2024+
// Database.changesCount calls sqlite3_changes(), whose documentation says:
2025+
//
2026+
// > https://sqlite.org/c3ref/changes.html
2027+
// > Changes to a view that are intercepted by INSTEAD OF triggers are not counted.
2028+
//
2029+
// We want to support INSTEAD OF triggers, so we prefer to use
2030+
// sqlite3_total_changes() for views.
2031+
//
2032+
// At the same time, FTS5 has sqlite3_total_changes() report changes
2033+
// even when database is not changed (https://github.com/groue/GRDB.swift/issues/1820)
2034+
//
2035+
// Well, let's do our best:
2036+
if try viewExists(tableName) {
2037+
let prevCount = totalChangesCount
2038+
let result = try updates()
2039+
count = totalChangesCount - prevCount
2040+
return result
2041+
} else {
2042+
let result = try updates()
2043+
count = changesCount
2044+
return result
2045+
}
2046+
}
2047+
}
2048+
20212049
extension Database {
20222050

20232051
// MARK: - Database-Related Types

GRDB/QueryInterface/Request/QueryInterfaceRequest.swift

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -635,9 +635,11 @@ extension QueryInterfaceRequest {
635635
@discardableResult
636636
public func deleteAll(_ db: Database) throws -> Int {
637637
let statement = try SQLQueryGenerator(relation: relation).makeDeleteStatement(db)
638-
let prevCount = db.totalChangesCount
639-
try statement.execute()
640-
return db.totalChangesCount - prevCount
638+
var changesCount = 0
639+
try db.countChanges(&changesCount, forTable: relation.source.tableName) {
640+
try statement.execute()
641+
}
642+
return changesCount
641643
}
642644
}
643645

@@ -1136,9 +1138,11 @@ extension QueryInterfaceRequest {
11361138
// database not hit
11371139
return 0
11381140
}
1139-
let prevCount = db.totalChangesCount
1140-
try updateStatement.execute()
1141-
return db.totalChangesCount - prevCount
1141+
var changesCount = 0
1142+
try db.countChanges(&changesCount, forTable: relation.source.tableName) {
1143+
try updateStatement.execute()
1144+
}
1145+
return changesCount
11421146
}
11431147

11441148
/// Updates matching rows, and returns the number of updated rows.

GRDB/Record/MutablePersistableRecord+Delete.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,10 @@ extension MutablePersistableRecord {
5353
// Nil primary key
5454
return false
5555
}
56-
let prevCount = db.totalChangesCount
57-
try statement.execute()
58-
return (db.totalChangesCount - prevCount) > 0
56+
var changesCount = 0
57+
try db.countChanges(&changesCount, forTable: type(of: self).databaseTableName) {
58+
try statement.execute()
59+
}
60+
return changesCount > 0
5961
}
6062
}

GRDB/Record/MutablePersistableRecord+Update.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -956,9 +956,10 @@ extension MutablePersistableRecord {
956956
// Nil primary key
957957
try dao.recordNotFound()
958958
}
959-
let prevCount = db.totalChangesCount
960-
let returned = try fetch(statement)
961-
let changesCount = db.totalChangesCount - prevCount
959+
var changesCount = 0
960+
let returned = try db.countChanges(&changesCount, forTable: type(of: self).databaseTableName) {
961+
try fetch(statement)
962+
}
962963
if changesCount == 0 {
963964
// No row was updated
964965
try dao.recordNotFound()

Tests/GRDBTests/MutablePersistableRecordTests.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2796,3 +2796,47 @@ extension MutablePersistableRecordTests {
27962796
} catch DatabaseError.SQLITE_MISUSE { }
27972797
}
27982798
}
2799+
2800+
#if SQLITE_ENABLE_FTS5
2801+
class Issue1820Tests: GRDBTestCase {
2802+
// Regression test for https://github.com/groue/GRDB.swift/issues/1820
2803+
func testIssue1820() throws {
2804+
struct Serving: Codable, FetchableRecord, PersistableRecord {
2805+
let id: UUID
2806+
var description: String
2807+
var foodId: String
2808+
2809+
static let author = hasOne(Food.self)
2810+
}
2811+
2812+
struct Food: Codable, FetchableRecord, PersistableRecord {
2813+
let id: UUID
2814+
var name: String
2815+
var foodId: String
2816+
}
2817+
2818+
let dbQueue = try makeDatabaseQueue()
2819+
try dbQueue.write { db in
2820+
try db.create(table: "food") { t in
2821+
t.column("id", .blob).primaryKey()
2822+
t.column("name", .text)
2823+
t.column("foodId", .text).unique()
2824+
}
2825+
2826+
try db.create(table: "serving") { t in
2827+
t.column("id", .blob).primaryKey()
2828+
t.column("description", .text)
2829+
t.column("foodId", .text).references("food", column: "foodId")
2830+
}
2831+
2832+
try db.create(virtualTable: "food_fts", using: FTS5()) { t in
2833+
t.synchronize(withTable: "food")
2834+
t.column("name")
2835+
}
2836+
2837+
try Food(id: UUID(), name: "Apple", foodId: "apple").save(db)
2838+
try Serving(id: UUID(), description: "Apple", foodId: "apple").save(db)
2839+
}
2840+
}
2841+
}
2842+
#endif

0 commit comments

Comments
 (0)