262 lines
8.3 KiB
Swift
262 lines
8.3 KiB
Swift
import XCTest
|
|
import GRDB
|
|
|
|
private struct A: Codable, FetchableRecord, PersistableRecord {
|
|
static let b = belongsTo(B.self)
|
|
static let c = hasOne(C.self, through: b, using: B.c)
|
|
var id: Int64
|
|
var bId: Int64?
|
|
var name: String
|
|
}
|
|
|
|
private struct B: Codable, FetchableRecord, PersistableRecord {
|
|
static let c = belongsTo(C.self)
|
|
var id: Int64
|
|
var cId: Int64?
|
|
var name: String
|
|
}
|
|
|
|
private struct C: Codable, FetchableRecord, PersistableRecord {
|
|
var id: Int64
|
|
var name: String
|
|
}
|
|
|
|
private struct AWithRequiredC: FetchableRecord {
|
|
var a: A
|
|
var c: C
|
|
|
|
init(row: Row) throws {
|
|
a = try A(row: row)
|
|
c = row["c"]
|
|
}
|
|
}
|
|
|
|
private struct AWithOptionalC: FetchableRecord {
|
|
var a: A
|
|
var c: C?
|
|
|
|
init(row: Row) throws {
|
|
a = try A(row: row)
|
|
c = row["c"]
|
|
}
|
|
}
|
|
|
|
private struct AWithRequiredBAndOptionalC: FetchableRecord {
|
|
var a: A
|
|
var b: B
|
|
var c: C?
|
|
|
|
init(row: Row) throws {
|
|
a = try A(row: row)
|
|
b = row["b"]
|
|
c = row["c"]
|
|
}
|
|
}
|
|
|
|
private struct AWithCName: FetchableRecord {
|
|
var a: A
|
|
var cName: String?
|
|
|
|
init(row: Row) throws {
|
|
a = try A(row: row)
|
|
cName = row["cName"]
|
|
}
|
|
}
|
|
|
|
private struct AWithRequiredBNameAndOptionalCName: FetchableRecord {
|
|
var a: A
|
|
var bName: String
|
|
var cName: String?
|
|
|
|
init(row: Row) throws {
|
|
a = try A(row: row)
|
|
bName = row["bName"]
|
|
cName = row["cName"]
|
|
}
|
|
}
|
|
|
|
/// Test support for FetchableRecord records
|
|
class AssociationHasOneThroughFetchableRecordTests: GRDBTestCase {
|
|
|
|
override func setup(_ dbWriter: some DatabaseWriter) throws {
|
|
try dbWriter.write { db in
|
|
try db.create(table: "c") { t in
|
|
t.autoIncrementedPrimaryKey("id")
|
|
t.column("name", .text)
|
|
}
|
|
try db.create(table: "b") { t in
|
|
t.autoIncrementedPrimaryKey("id")
|
|
t.belongsTo("c")
|
|
t.column("name", .text)
|
|
}
|
|
try db.create(table: "a") { t in
|
|
t.autoIncrementedPrimaryKey("id")
|
|
t.belongsTo("b")
|
|
t.column("name", .text)
|
|
}
|
|
|
|
try C(id: 1, name: "c1").insert(db)
|
|
try B(id: 1, cId: 1, name: "b1").insert(db)
|
|
try B(id: 2, cId: nil, name: "b2").insert(db)
|
|
try A(id: 1, bId: 1, name: "a1").insert(db)
|
|
try A(id: 2, bId: 2, name: "a2").insert(db)
|
|
try A(id: 3, bId: nil, name: "a3").insert(db)
|
|
}
|
|
}
|
|
|
|
func testIncludingRequired() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = A.including(required: A.c).order(sql: "a.id")
|
|
let records = try dbQueue.inDatabase { try AWithRequiredC.fetchAll($0, request) }
|
|
|
|
XCTAssertEqual(records.count, 1)
|
|
|
|
XCTAssertEqual(records[0].a.id, 1)
|
|
XCTAssertEqual(records[0].a.bId, 1)
|
|
XCTAssertEqual(records[0].a.name, "a1")
|
|
XCTAssertEqual(records[0].c.id, 1)
|
|
XCTAssertEqual(records[0].c.name, "c1")
|
|
}
|
|
|
|
func testAnnotatedWithRequired() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = A.annotated(withRequired: A.c.select(Column("name").forKey("cName"))).order(sql: "a.id")
|
|
let records = try dbQueue.inDatabase { try AWithCName.fetchAll($0, request) }
|
|
|
|
XCTAssertEqual(records.count, 1)
|
|
|
|
XCTAssertEqual(records[0].a.id, 1)
|
|
XCTAssertEqual(records[0].a.bId, 1)
|
|
XCTAssertEqual(records[0].a.name, "a1")
|
|
XCTAssertEqual(records[0].cName, "c1")
|
|
}
|
|
|
|
func testIncludingOptional() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = A.including(optional: A.c).order(sql: "a.id")
|
|
let records = try dbQueue.inDatabase { try AWithOptionalC.fetchAll($0, request) }
|
|
|
|
XCTAssertEqual(records.count, 3)
|
|
|
|
XCTAssertEqual(records[0].a.id, 1)
|
|
XCTAssertEqual(records[0].a.bId, 1)
|
|
XCTAssertEqual(records[0].a.name, "a1")
|
|
XCTAssertEqual(records[0].c!.id, 1)
|
|
XCTAssertEqual(records[0].c!.name, "c1")
|
|
|
|
XCTAssertEqual(records[1].a.id, 2)
|
|
XCTAssertEqual(records[1].a.bId, 2)
|
|
XCTAssertEqual(records[1].a.name, "a2")
|
|
XCTAssertNil(records[1].c)
|
|
|
|
XCTAssertEqual(records[2].a.id, 3)
|
|
XCTAssertNil(records[2].a.bId)
|
|
XCTAssertEqual(records[2].a.name, "a3")
|
|
XCTAssertNil(records[2].c)
|
|
}
|
|
|
|
func testAnnotatedWithOptional() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = A.annotated(withOptional: A.c.select(Column("name").forKey("cName"))).order(sql: "a.id")
|
|
let records = try dbQueue.inDatabase { try AWithCName.fetchAll($0, request) }
|
|
|
|
XCTAssertEqual(records.count, 3)
|
|
|
|
XCTAssertEqual(records[0].a.id, 1)
|
|
XCTAssertEqual(records[0].a.bId, 1)
|
|
XCTAssertEqual(records[0].a.name, "a1")
|
|
XCTAssertEqual(records[0].cName, "c1")
|
|
|
|
XCTAssertEqual(records[1].a.id, 2)
|
|
XCTAssertEqual(records[1].a.bId, 2)
|
|
XCTAssertEqual(records[1].a.name, "a2")
|
|
XCTAssertNil(records[1].cName)
|
|
|
|
XCTAssertEqual(records[2].a.id, 3)
|
|
XCTAssertNil(records[2].a.bId)
|
|
XCTAssertEqual(records[2].a.name, "a3")
|
|
XCTAssertNil(records[2].cName)
|
|
}
|
|
|
|
func testJoiningRequired() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = A.joining(required: A.c).order(sql: "a.id")
|
|
let records = try dbQueue.inDatabase { try A.fetchAll($0, request) }
|
|
|
|
XCTAssertEqual(records.count, 1)
|
|
|
|
XCTAssertEqual(records[0].id, 1)
|
|
XCTAssertEqual(records[0].bId, 1)
|
|
XCTAssertEqual(records[0].name, "a1")
|
|
}
|
|
|
|
func testJoiningOptional() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = A.joining(optional: A.c).order(sql: "a.id")
|
|
let records = try dbQueue.inDatabase { try A.fetchAll($0, request) }
|
|
|
|
XCTAssertEqual(records.count, 3)
|
|
|
|
XCTAssertEqual(records[0].id, 1)
|
|
XCTAssertEqual(records[0].bId, 1)
|
|
XCTAssertEqual(records[0].name, "a1")
|
|
|
|
XCTAssertEqual(records[1].id, 2)
|
|
XCTAssertEqual(records[1].bId, 2)
|
|
XCTAssertEqual(records[1].name, "a2")
|
|
|
|
XCTAssertEqual(records[2].id, 3)
|
|
XCTAssertNil(records[2].bId)
|
|
XCTAssertEqual(records[2].name, "a3")
|
|
}
|
|
|
|
func testIncludingOptionalIncludingRequiredPivot() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = A
|
|
.including(optional: A.c)
|
|
.including(required: A.b)
|
|
.order(sql: "a.id")
|
|
let records = try dbQueue.inDatabase { try AWithRequiredBAndOptionalC.fetchAll($0, request) }
|
|
|
|
XCTAssertEqual(records.count, 2)
|
|
|
|
XCTAssertEqual(records[0].a.id, 1)
|
|
XCTAssertEqual(records[0].a.bId, 1)
|
|
XCTAssertEqual(records[0].a.name, "a1")
|
|
XCTAssertEqual(records[0].b.id, 1)
|
|
XCTAssertEqual(records[0].b.name, "b1")
|
|
XCTAssertEqual(records[0].c!.id, 1)
|
|
XCTAssertEqual(records[0].c!.name, "c1")
|
|
|
|
XCTAssertEqual(records[1].a.id, 2)
|
|
XCTAssertEqual(records[1].a.bId, 2)
|
|
XCTAssertEqual(records[1].a.name, "a2")
|
|
XCTAssertEqual(records[1].b.id, 2)
|
|
XCTAssertEqual(records[1].b.name, "b2")
|
|
XCTAssertNil(records[1].c)
|
|
}
|
|
|
|
func testAnnotatedWithOptionalAnnotatedWithRequiredPivot() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
let request = A
|
|
.annotated(withOptional: A.c.select(Column("name").forKey("cName")))
|
|
.annotated(withRequired: A.b.select(Column("name").forKey("bName")))
|
|
.order(sql: "a.id")
|
|
let records = try dbQueue.inDatabase { try AWithRequiredBNameAndOptionalCName.fetchAll($0, request) }
|
|
|
|
XCTAssertEqual(records.count, 2)
|
|
|
|
XCTAssertEqual(records[0].a.id, 1)
|
|
XCTAssertEqual(records[0].a.bId, 1)
|
|
XCTAssertEqual(records[0].a.name, "a1")
|
|
XCTAssertEqual(records[0].bName, "b1")
|
|
XCTAssertEqual(records[0].cName, "c1")
|
|
|
|
XCTAssertEqual(records[1].a.id, 2)
|
|
XCTAssertEqual(records[1].a.bId, 2)
|
|
XCTAssertEqual(records[1].a.name, "a2")
|
|
XCTAssertEqual(records[1].bName, "b2")
|
|
XCTAssertNil(records[1].cName)
|
|
}
|
|
}
|