Files
UUVPN/iOS-SwiftUI-Code/GRDB.swift-6.29.3/Tests/GRDBTests/TableRecordUpdateTests.swift
T
2025-01-28 12:28:03 +08:00

787 lines
32 KiB
Swift

import XCTest
import GRDB
private struct Player: Codable, PersistableRecord, FetchableRecord, Hashable {
var id: Int64
var name: String
var score: Int
var bonus: Int
static func createTable(_ db: Database) throws {
try db.create(table: "player") { t in
t.autoIncrementedPrimaryKey("id")
t.column("name", .text)
t.column("score", .integer)
t.column("bonus", .integer)
}
}
}
@available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *)
extension Player: Identifiable { }
private enum Columns: String, ColumnExpression {
case id, name, score, bonus
}
private extension QueryInterfaceRequest<Player> {
func incrementScore(_ db: Database) throws {
try updateAll(db, Columns.score += 1)
}
}
class TableRecordUpdateTests: GRDBTestCase {
func testRequestUpdateAll() throws {
try makeDatabaseQueue().write { db in
try Player.createTable(db)
let assignment = Columns.score.set(to: 0)
try Player.updateAll(db, assignment)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0
""")
try Player.filter(Columns.name == "Arthur").updateAll(db, assignment)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0 WHERE \"name\" = 'Arthur'
""")
try Player.filter(key: 1).updateAll(db, assignment)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0 WHERE "id" = 1
""")
try Player.filter(keys: [1, 2]).updateAll(db, assignment)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0 WHERE "id" IN (1, 2)
""")
if #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) {
try Player.filter(id: 1).updateAll(db, assignment)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0 WHERE "id" = 1
""")
try Player.filter(ids: [1, 2]).updateAll(db, assignment)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0 WHERE "id" IN (1, 2)
""")
}
try Player.filter(sql: "id = 1").updateAll(db, assignment)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0 WHERE id = 1
""")
try Player.filter(sql: "id = 1").filter(Columns.name == "Arthur").updateAll(db, assignment)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0 WHERE (id = 1) AND (\"name\" = 'Arthur')
""")
try Player.select(Columns.name).updateAll(db, assignment)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0
""")
try Player.order(Columns.name).updateAll(db, assignment)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0
""")
if try String.fetchCursor(db, sql: "PRAGMA COMPILE_OPTIONS").contains("ENABLE_UPDATE_DELETE_LIMIT") {
try Player.limit(1).updateAll(db, assignment)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0 LIMIT 1
""")
try Player.order(Columns.name).updateAll(db, assignment)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0
""")
try Player.order(Columns.name).limit(1).updateAll(db, assignment)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0 ORDER BY \"name\" LIMIT 1
""")
try Player.order(Columns.name).limit(1, offset: 2).reversed().updateAll(db, assignment)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0 ORDER BY \"name\" DESC LIMIT 1 OFFSET 2
""")
try Player.limit(1, offset: 2).reversed().updateAll(db, assignment)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0 LIMIT 1 OFFSET 2
""")
}
}
}
func testRequestUpdateAndFetchStatement() throws {
#if GRDBCUSTOMSQLITE || GRDBCIPHER
guard sqlite3_libversion_number() >= 3035000 else {
throw XCTSkip("RETURNING clause is not available")
}
#else
guard #available(iOS 15, macOS 12, tvOS 15, watchOS 8, *) else {
throw XCTSkip("RETURNING clause is not available")
}
#endif
try makeDatabaseQueue().write { db in
try Player.createTable(db)
let assignment = Columns.score.set(to: 0)
let request = Player.all()
let statement = try request.updateAndFetchStatement(db, [assignment], selection: [AllColumns()])
XCTAssertEqual(statement.sql, "UPDATE \"player\" SET \"score\" = ? RETURNING *")
XCTAssertEqual(statement.arguments, [0])
XCTAssertEqual(statement.columnNames, ["id", "name", "score", "bonus"])
}
}
func testRequestUpdateAndFetchCursor() throws {
#if GRDBCUSTOMSQLITE || GRDBCIPHER
guard sqlite3_libversion_number() >= 3035000 else {
throw XCTSkip("RETURNING clause is not available")
}
#else
guard #available(iOS 15, macOS 12, tvOS 15, watchOS 8, *) else {
throw XCTSkip("RETURNING clause is not available")
}
#endif
try makeDatabaseQueue().write { db in
try Player.createTable(db)
try Player(id: 1, name: "Arthur", score: 10, bonus: 0).insert(db)
try Player(id: 2, name: "Barbara", score: 20, bonus: 10).insert(db)
try Player(id: 3, name: "Craig", score: 30, bonus: 20).insert(db)
let request = Player.filter(Columns.id != 2)
let cursor = try request.updateAndFetchCursor(db, [Columns.score += 100])
let updatedPlayers = try Array(cursor).sorted(by: { $0.id < $1.id })
XCTAssertEqual(updatedPlayers, [
Player(id: 1, name: "Arthur", score: 110, bonus: 0),
Player(id: 3, name: "Craig", score: 130, bonus: 20),
])
}
}
func testRequestUpdateAndFetchAll() throws {
#if GRDBCUSTOMSQLITE || GRDBCIPHER
guard sqlite3_libversion_number() >= 3035000 else {
throw XCTSkip("RETURNING clause is not available")
}
#else
guard #available(iOS 15, macOS 12, tvOS 15, watchOS 8, *) else {
throw XCTSkip("RETURNING clause is not available")
}
#endif
try makeDatabaseQueue().write { db in
try Player.createTable(db)
try Player(id: 1, name: "Arthur", score: 10, bonus: 0).insert(db)
try Player(id: 2, name: "Barbara", score: 20, bonus: 10).insert(db)
try Player(id: 3, name: "Craig", score: 30, bonus: 20).insert(db)
let request = Player.filter(Columns.id != 2)
let updatedPlayers = try request
.updateAndFetchAll(db, [Columns.score += 100])
.sorted(by: { $0.id < $1.id })
XCTAssertEqual(updatedPlayers, [
Player(id: 1, name: "Arthur", score: 110, bonus: 0),
Player(id: 3, name: "Craig", score: 130, bonus: 20),
])
}
}
func testRequestUpdateAndFetchSet() throws {
#if GRDBCUSTOMSQLITE || GRDBCIPHER
guard sqlite3_libversion_number() >= 3035000 else {
throw XCTSkip("RETURNING clause is not available")
}
#else
guard #available(iOS 15, macOS 12, tvOS 15, watchOS 8, *) else {
throw XCTSkip("RETURNING clause is not available")
}
#endif
try makeDatabaseQueue().write { db in
try Player.createTable(db)
try Player(id: 1, name: "Arthur", score: 10, bonus: 0).insert(db)
try Player(id: 2, name: "Barbara", score: 20, bonus: 10).insert(db)
try Player(id: 3, name: "Craig", score: 30, bonus: 20).insert(db)
let request = Player.filter(Columns.id != 2)
let updatedPlayers = try request.updateAndFetchSet(db, [Columns.score += 100])
XCTAssertEqual(updatedPlayers, [
Player(id: 1, name: "Arthur", score: 110, bonus: 0),
Player(id: 3, name: "Craig", score: 130, bonus: 20),
])
}
}
func testNilAssignment() throws {
try makeDatabaseQueue().write { db in
try Player.createTable(db)
try Player.updateAll(db, Columns.score.set(to: nil))
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = NULL
""")
}
}
func testComplexAssignment() throws {
try makeDatabaseQueue().write { db in
try Player.createTable(db)
try Player.updateAll(db, Columns.score.set(to: Columns.score * (Columns.bonus + 1)))
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" * ("bonus" + 1)
""")
}
}
func testAssignmentSubtractAndAssign() throws {
try makeDatabaseQueue().write { db in
try Player.createTable(db)
try Player.updateAll(db, Columns.score -= 1)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" - 1
""")
try Player.updateAll(db, Columns.score -= Columns.bonus)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" - "bonus"
""")
try Player.updateAll(db, Columns.score -= -Columns.bonus)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" - (-"bonus")
""")
try Player.updateAll(db, Columns.score -= Columns.bonus * 2)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" - ("bonus" * 2)
""")
}
}
func testAssignmentAddAndAssign() throws {
try makeDatabaseQueue().write { db in
try Player.createTable(db)
try Player.updateAll(db, Columns.score += 1)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" + 1
""")
try Player.updateAll(db, Columns.score += Columns.bonus)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" + "bonus"
""")
try Player.updateAll(db, Columns.score += -Columns.bonus)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" + (-"bonus")
""")
try Player.updateAll(db, Columns.score += Columns.bonus * 2)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" + ("bonus" * 2)
""")
}
}
func testAssignmentMultiplyAndAssign() throws {
try makeDatabaseQueue().write { db in
try Player.createTable(db)
try Player.updateAll(db, Columns.score *= 1)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" * 1
""")
try Player.updateAll(db, Columns.score *= Columns.bonus)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" * "bonus"
""")
try Player.updateAll(db, Columns.score *= -Columns.bonus)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" * (-"bonus")
""")
try Player.updateAll(db, Columns.score *= Columns.bonus * 2)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" * ("bonus" * 2)
""")
}
}
func testAssignmentDivideAndAssign() throws {
try makeDatabaseQueue().write { db in
try Player.createTable(db)
try Player.updateAll(db, Columns.score /= 1)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" / 1
""")
try Player.updateAll(db, Columns.score /= Columns.bonus)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" / "bonus"
""")
try Player.updateAll(db, Columns.score /= -Columns.bonus)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" / (-"bonus")
""")
try Player.updateAll(db, Columns.score /= Columns.bonus * 2)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" / ("bonus" * 2)
""")
}
}
func testAssignmentBitwiseAndAssign() throws {
try makeDatabaseQueue().write { db in
try Player.createTable(db)
try Player.updateAll(db, Columns.score &= 1)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" & 1
""")
try Player.updateAll(db, Columns.score &= Columns.bonus)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" & "bonus"
""")
try Player.updateAll(db, Columns.score &= -Columns.bonus)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" & (-"bonus")
""")
try Player.updateAll(db, Columns.score &= Columns.bonus * 2)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" & ("bonus" * 2)
""")
}
}
func testAssignmentBitwiseOrAssign() throws {
try makeDatabaseQueue().write { db in
try Player.createTable(db)
try Player.updateAll(db, Columns.score |= 1)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" | 1
""")
try Player.updateAll(db, Columns.score |= Columns.bonus)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" | "bonus"
""")
try Player.updateAll(db, Columns.score |= -Columns.bonus)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" | (-"bonus")
""")
try Player.updateAll(db, Columns.score |= Columns.bonus * 2)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" | ("bonus" * 2)
""")
}
}
func testAssignmentLeftShiftAssign() throws {
try makeDatabaseQueue().write { db in
try Player.createTable(db)
try Player.updateAll(db, Columns.score <<= 1)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" << 1
""")
try Player.updateAll(db, Columns.score <<= Columns.bonus)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" << "bonus"
""")
try Player.updateAll(db, Columns.score <<= -Columns.bonus)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" << (-"bonus")
""")
try Player.updateAll(db, Columns.score <<= Columns.bonus * 2)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" << ("bonus" * 2)
""")
}
}
func testAssignmentRightShiftAssign() throws {
try makeDatabaseQueue().write { db in
try Player.createTable(db)
try Player.updateAll(db, Columns.score >>= 1)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" >> 1
""")
try Player.updateAll(db, Columns.score >>= Columns.bonus)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" >> "bonus"
""")
try Player.updateAll(db, Columns.score >>= -Columns.bonus)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" >> (-"bonus")
""")
try Player.updateAll(db, Columns.score >>= Columns.bonus * 2)
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = "score" >> ("bonus" * 2)
""")
}
}
func testMultipleAssignments() throws {
try makeDatabaseQueue().write { db in
try Player.createTable(db)
try Player.updateAll(db, Columns.score.set(to: 0), Columns.bonus.set(to: 1))
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0, "bonus" = 1
""")
try Player.updateAll(db, [Columns.score.set(to: 0), Columns.bonus.set(to: 1)])
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0, "bonus" = 1
""")
try Player.all().updateAll(db, Columns.score.set(to: 0), Columns.bonus.set(to: 1))
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0, "bonus" = 1
""")
try Player.all().updateAll(db, [Columns.score.set(to: 0), Columns.bonus.set(to: 1)])
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0, "bonus" = 1
""")
}
}
func testUpdateAllWithoutAssignmentDoesNotAccessTheDatabase() throws {
try makeDatabaseQueue().write { db in
try Player.createTable(db)
sqlQueries.removeAll()
try XCTAssertEqual(Player.updateAll(db, []), 0)
try XCTAssertEqual(Player.all().updateAll(db, []), 0)
XCTAssert(sqlQueries.isEmpty)
}
}
func testUpdateAllReturnsNumberOfUpdatedRows() throws {
try makeDatabaseQueue().write { db in
try Player.createTable(db)
try db.execute(sql: """
INSERT INTO player (id, name, score, bonus) VALUES (1, 'Arthur', 0, 2);
INSERT INTO player (id, name, score, bonus) VALUES (2, 'Barbara', 0, 1);
INSERT INTO player (id, name, score, bonus) VALUES (3, 'Craig', 0, 0);
INSERT INTO player (id, name, score, bonus) VALUES (4, 'Diane', 0, 3);
""")
let assignment = Columns.score += 1
try XCTAssertEqual(Player.updateAll(db, assignment), 4)
try XCTAssertEqual(Player.filter(key: 1).updateAll(db, assignment), 1)
try XCTAssertEqual(Player.filter(key: 5).updateAll(db, assignment), 0)
try XCTAssertEqual(Player.filter(Columns.bonus > 1).updateAll(db, assignment), 2)
if try String.fetchCursor(db, sql: "PRAGMA COMPILE_OPTIONS").contains("ENABLE_UPDATE_DELETE_LIMIT") {
try XCTAssertEqual(Player.limit(1).updateAll(db, assignment), 1)
try XCTAssertEqual(Player.limit(2).updateAll(db, assignment), 2)
try XCTAssertEqual(Player.limit(2, offset: 3).updateAll(db, assignment), 1)
try XCTAssertEqual(Player.limit(10).updateAll(db, assignment), 4)
}
}
}
func testQueryInterfaceExtension() throws {
try makeDatabaseQueue().write { db in
try Player.createTable(db)
try db.execute(sql: """
INSERT INTO player (id, name, score, bonus) VALUES (1, 'Arthur', 0, 0);
INSERT INTO player (id, name, score, bonus) VALUES (2, 'Barbara', 0, 0);
INSERT INTO player (id, name, score, bonus) VALUES (3, 'Craig', 0, 0);
INSERT INTO player (id, name, score, bonus) VALUES (4, 'Diane', 0, 0);
""")
try Player.all().incrementScore(db)
try XCTAssertEqual(Player.filter(Columns.score == 1).fetchCount(db), 4)
try Player.filter(key: 1).incrementScore(db)
try XCTAssertEqual(Player.fetchOne(db, key: 1)!.score, 2)
}
}
func testConflictPolicyAbort() throws {
struct AbortPlayer: PersistableRecord {
static let databaseTableName = "player"
static let persistenceConflictPolicy = PersistenceConflictPolicy(insert: .abort, update: .abort)
func encode(to container: inout PersistenceContainer) { }
}
try makeDatabaseQueue().write { db in
try Player.createTable(db)
try AbortPlayer.updateAll(db, Column("score").set(to: 0))
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0
""")
try AbortPlayer.updateAll(db, [Column("score").set(to: 0)])
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0
""")
try AbortPlayer.all().updateAll(db, Column("score").set(to: 0))
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0
""")
try AbortPlayer.all().updateAll(db, [Column("score").set(to: 0)])
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0
""")
}
}
func testConflictPolicyIgnore() throws {
struct IgnorePlayer: PersistableRecord {
static let databaseTableName = "player"
static let persistenceConflictPolicy = PersistenceConflictPolicy(insert: .abort, update: .ignore)
func encode(to container: inout PersistenceContainer) { }
}
try makeDatabaseQueue().write { db in
try Player.createTable(db)
try IgnorePlayer.updateAll(db, Column("score").set(to: 0))
XCTAssertEqual(self.lastSQLQuery, """
UPDATE OR IGNORE "player" SET "score" = 0
""")
try IgnorePlayer.updateAll(db, [Column("score").set(to: 0)])
XCTAssertEqual(self.lastSQLQuery, """
UPDATE OR IGNORE "player" SET "score" = 0
""")
try IgnorePlayer.all().updateAll(db, Column("score").set(to: 0))
XCTAssertEqual(self.lastSQLQuery, """
UPDATE OR IGNORE "player" SET "score" = 0
""")
try IgnorePlayer.all().updateAll(db, [Column("score").set(to: 0)])
XCTAssertEqual(self.lastSQLQuery, """
UPDATE OR IGNORE "player" SET "score" = 0
""")
}
}
func testConflictPolicyIgnoreWithTable() throws {
struct IgnorePlayer: PersistableRecord {
static let databaseTableName = "player"
static let persistenceConflictPolicy = PersistenceConflictPolicy(insert: .abort, update: .ignore)
func encode(to container: inout PersistenceContainer) { }
}
let table = Table<IgnorePlayer>("player")
try makeDatabaseQueue().write { db in
try Player.createTable(db)
try table.updateAll(db, Column("score").set(to: 0))
XCTAssertEqual(self.lastSQLQuery, """
UPDATE OR IGNORE "player" SET "score" = 0
""")
try table.updateAll(db, [Column("score").set(to: 0)])
XCTAssertEqual(self.lastSQLQuery, """
UPDATE OR IGNORE "player" SET "score" = 0
""")
try table.all().updateAll(db, Column("score").set(to: 0))
XCTAssertEqual(self.lastSQLQuery, """
UPDATE OR IGNORE "player" SET "score" = 0
""")
try table.all().updateAll(db, [Column("score").set(to: 0)])
XCTAssertEqual(self.lastSQLQuery, """
UPDATE OR IGNORE "player" SET "score" = 0
""")
}
}
func testConflictPolicyCustom() throws {
try makeDatabaseQueue().write { db in
try Player.createTable(db)
try Player.updateAll(db, Column("score").set(to: 0))
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0
""")
try Player.updateAll(db, onConflict: .ignore, Column("score").set(to: 0))
XCTAssertEqual(self.lastSQLQuery, """
UPDATE OR IGNORE "player" SET "score" = 0
""")
try Player.updateAll(db, onConflict: .ignore, [Column("score").set(to: 0)])
XCTAssertEqual(self.lastSQLQuery, """
UPDATE OR IGNORE "player" SET "score" = 0
""")
try Player.all().updateAll(db, onConflict: .ignore, Column("score").set(to: 0))
XCTAssertEqual(self.lastSQLQuery, """
UPDATE OR IGNORE "player" SET "score" = 0
""")
try Player.all().updateAll(db, onConflict: .ignore, [Column("score").set(to: 0)])
XCTAssertEqual(self.lastSQLQuery, """
UPDATE OR IGNORE "player" SET "score" = 0
""")
}
}
func testJoinedRequestUpdate() throws {
try makeDatabaseQueue().inDatabase { db in
struct Player: MutablePersistableRecord {
static let team = belongsTo(Team.self)
func encode(to container: inout PersistenceContainer) { preconditionFailure("should not be called") }
}
struct Team: MutablePersistableRecord {
static let players = hasMany(Player.self)
func encode(to container: inout PersistenceContainer) { preconditionFailure("should not be called") }
}
try db.create(table: "team") { t in
t.autoIncrementedPrimaryKey("id")
t.column("active", .boolean)
}
try db.create(table: "player") { t in
t.autoIncrementedPrimaryKey("id")
t.belongsTo("team")
t.column("score", .integer)
}
do {
try Player.including(required: Player.team).updateAll(db, Column("score").set(to: 0))
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0 WHERE "id" IN (\
SELECT "player"."id" \
FROM "player" \
JOIN "team" ON "team"."id" = "player"."teamId")
""")
}
do {
// Regression test for https://github.com/groue/GRDB.swift/issues/758
try Player.including(required: Player.team.filter(Column("active") == 1)).updateAll(db, Column("score").set(to: 0))
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0 WHERE "id" IN (\
SELECT "player"."id" \
FROM "player" \
JOIN "team" ON ("team"."id" = "player"."teamId") AND ("team"."active" = 1))
""")
}
do {
let alias = TableAlias(name: "p")
try Player.aliased(alias).including(required: Player.team).updateAll(db, Column("score").set(to: 0))
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0 WHERE "id" IN (\
SELECT "p"."id" \
FROM "player" "p" \
JOIN "team" ON "team"."id" = "p"."teamId")
""")
}
do {
try Team.having(Team.players.isEmpty).updateAll(db, Column("active").set(to: false))
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "team" SET "active" = 0 WHERE "id" IN (\
SELECT "team"."id" \
FROM "team" \
LEFT JOIN "player" ON "player"."teamId" = "team"."id" \
GROUP BY "team"."id" \
HAVING COUNT(DISTINCT "player"."id") = 0)
""")
}
do {
try Team.including(all: Team.players).updateAll(db, Column("active").set(to: false))
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "team" SET "active" = 0
""")
}
}
}
func testGroupedRequestUpdate() throws {
try makeDatabaseQueue().inDatabase { db in
struct Player: MutablePersistableRecord {
func encode(to container: inout PersistenceContainer) { preconditionFailure("should not be called") }
}
struct Passport: MutablePersistableRecord {
func encode(to container: inout PersistenceContainer) { preconditionFailure("should not be called") }
}
try db.create(table: "player") { t in
t.autoIncrementedPrimaryKey("id")
t.column("score", .integer)
}
try db.create(table: "passport") { t in
t.column("countryCode", .text).notNull()
t.column("citizenId", .integer).notNull()
t.column("active", .boolean)
t.primaryKey(["countryCode", "citizenId"])
}
do {
try Player.all().groupByPrimaryKey().updateAll(db, Column("score").set(to: 0))
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0 WHERE "id" IN (\
SELECT "id" \
FROM "player" \
GROUP BY "id")
""")
}
do {
try Player.all().group(Column.rowID).updateAll(db, Column("score").set(to: 0))
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "player" SET "score" = 0 WHERE "id" IN (\
SELECT "id" \
FROM "player" \
GROUP BY "rowid")
""")
}
do {
try Passport.all().groupByPrimaryKey().updateAll(db, Column("active").set(to: true))
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "passport" SET "active" = 1 WHERE "rowid" IN (\
SELECT "rowid" \
FROM "passport" \
GROUP BY "rowid")
""")
}
do {
try Passport.all().group(Column.rowID).updateAll(db, Column("active").set(to: true))
XCTAssertEqual(self.lastSQLQuery, """
UPDATE "passport" SET "active" = 1 WHERE "rowid" IN (\
SELECT "rowid" \
FROM "passport" \
GROUP BY "rowid")
""")
}
}
}
}