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

1318 lines
54 KiB
Swift

import XCTest
import GRDB
private struct A: TableRecord, FetchableRecord, Equatable {
var cola1: Int64
var cola2: String
init(row: Row) {
cola1 = row["cola1"]
cola2 = row["cola2"]
}
}
private struct B: TableRecord, FetchableRecord, Hashable {
var colb1: Int64
var colb2: Int64?
var colb3: String
init(row: Row) {
colb1 = row["colb1"]
colb2 = row["colb2"]
colb3 = row["colb3"]
}
}
private struct C: TableRecord, FetchableRecord, Equatable {
var colc1: Int64
var colc2: Int64
init(row: Row) {
colc1 = row["colc1"]
colc2 = row["colc2"]
}
}
private struct D: TableRecord, FetchableRecord, Equatable {
var cold1: Int64
var cold2: Int64?
var cold3: String
init(row: Row) {
cold1 = row["cold1"]
cold2 = row["cold2"]
cold3 = row["cold3"]
}
}
class AssociationPrefetchingFetchableRecordTests: GRDBTestCase {
override func setup(_ dbWriter: some DatabaseWriter) throws {
try dbWriter.write { db in
try db.create(table: "a") { t in
t.autoIncrementedPrimaryKey("cola1")
t.column("cola2", .text)
}
try db.create(table: "b") { t in
t.autoIncrementedPrimaryKey("colb1")
t.column("colb2", .integer).references("a")
t.column("colb3", .text)
}
try db.create(table: "c") { t in
t.autoIncrementedPrimaryKey("colc1")
t.column("colc2", .integer).references("a")
}
try db.create(table: "d") { t in
t.autoIncrementedPrimaryKey("cold1")
t.column("cold2", .integer).references("c")
t.column("cold3", .text)
}
try db.execute(
sql: """
INSERT INTO a (cola1, cola2) VALUES (?, ?);
INSERT INTO a (cola1, cola2) VALUES (?, ?);
INSERT INTO a (cola1, cola2) VALUES (?, ?);
INSERT INTO b (colb1, colb2, colb3) VALUES (?, ?, ?);
INSERT INTO b (colb1, colb2, colb3) VALUES (?, ?, ?);
INSERT INTO b (colb1, colb2, colb3) VALUES (?, ?, ?);
INSERT INTO b (colb1, colb2, colb3) VALUES (?, ?, ?);
INSERT INTO c (colc1, colc2) VALUES (?, ?);
INSERT INTO c (colc1, colc2) VALUES (?, ?);
INSERT INTO c (colc1, colc2) VALUES (?, ?);
INSERT INTO d (cold1, cold2, cold3) VALUES (?, ?, ?);
INSERT INTO d (cold1, cold2, cold3) VALUES (?, ?, ?);
INSERT INTO d (cold1, cold2, cold3) VALUES (?, ?, ?);
INSERT INTO d (cold1, cold2, cold3) VALUES (?, ?, ?);
INSERT INTO d (cold1, cold2, cold3) VALUES (?, ?, ?);
""",
arguments: [
1, "a1",
2, "a2",
3, "a3",
4, 1, "b1",
5, 1, "b2",
6, 2, "b3",
14, nil, "b4",
7, 1,
8, 2,
9, 2,
10, 7, "d1",
11, 8, "d2",
12, 8, "d3",
13, 9, "d4",
14, nil, "d5",
])
}
}
func testIncludingAllHasMany() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.read { db in
// Plain request
do {
let request = A
.including(all: A
.hasMany(B.self)
.orderByPrimaryKey())
.orderByPrimaryKey()
// Array
do {
struct Record: FetchableRecord, Equatable {
var a: A
var bs: [B]
init(a: A, bs: [B]) {
self.a = a
self.bs = bs
}
init(row: Row) {
self.init(a: A(row: row), bs: row["bs"])
}
}
// Record.fetchAll
do {
let records = try Record.fetchAll(db, request)
XCTAssertEqual(records, [
Record(
a: A(row: ["cola1": 1, "cola2": "a1"]),
bs: [
B(row: ["colb1": 4, "colb2": 1, "colb3": "b1"]),
B(row: ["colb1": 5, "colb2": 1, "colb3": "b2"]),
]),
Record(
a: A(row: ["cola1": 2, "cola2": "a2"]),
bs: [
B(row: ["colb1": 6, "colb2": 2, "colb3": "b3"]),
]),
Record(
a: A(row: ["cola1": 3, "cola2": "a3"]),
bs: []),
])
}
// Record.fetchOne
do {
let record = try Record.fetchOne(db, request)!
XCTAssertEqual(record, Record(
a: A(row: ["cola1": 1, "cola2": "a1"]),
bs: [
B(row: ["colb1": 4, "colb2": 1, "colb3": "b1"]),
B(row: ["colb1": 5, "colb2": 1, "colb3": "b2"]),
]))
}
}
// Set
do {
struct Record: FetchableRecord, Equatable {
var a: A
var bs: Set<B>
init(a: A, bs: Set<B>) {
self.a = a
self.bs = bs
}
init(row: Row) {
self.init(a: A(row: row), bs: row["bs"])
}
}
// Record.fetchAll
do {
let records = try Record.fetchAll(db, request)
XCTAssertEqual(records, [
Record(
a: A(row: ["cola1": 1, "cola2": "a1"]),
bs: [
B(row: ["colb1": 4, "colb2": 1, "colb3": "b1"]),
B(row: ["colb1": 5, "colb2": 1, "colb3": "b2"]),
]),
Record(
a: A(row: ["cola1": 2, "cola2": "a2"]),
bs: [
B(row: ["colb1": 6, "colb2": 2, "colb3": "b3"]),
]),
Record(
a: A(row: ["cola1": 3, "cola2": "a3"]),
bs: []),
])
}
// Record.fetchOne
do {
let record = try Record.fetchOne(db, request)!
XCTAssertEqual(record, Record(
a: A(row: ["cola1": 1, "cola2": "a1"]),
bs: [
B(row: ["colb1": 4, "colb2": 1, "colb3": "b1"]),
B(row: ["colb1": 5, "colb2": 1, "colb3": "b2"]),
]))
}
}
}
// Request with filters
do {
let request = A
.filter(Column("cola1") != 3)
.including(all: A
.hasMany(B.self)
.filter(Column("colb1") == 4)
.orderByPrimaryKey()
.forKey("bs1"))
.including(all: A
.hasMany(B.self)
.filter(Column("colb1") != 4)
.orderByPrimaryKey()
.forKey("bs2"))
.orderByPrimaryKey()
struct Record: FetchableRecord, Equatable {
var a: A
var bs1: [B]
var bs2: [B]
init(a: A, bs1: [B], bs2: [B]) {
self.a = a
self.bs1 = bs1
self.bs2 = bs2
}
init(row: Row) {
self.init(a: A(row: row), bs1: row["bs1"], bs2: row["bs2"])
}
}
// Record.fetchAll
do {
let records = try Record.fetchAll(db, request)
XCTAssertEqual(records, [
Record(
a: A(row: ["cola1": 1, "cola2": "a1"]),
bs1: [
B(row: ["colb1": 4, "colb2": 1, "colb3": "b1"]),
],
bs2: [
B(row: ["colb1": 5, "colb2": 1, "colb3": "b2"]),
]),
Record(
a: A(row: ["cola1": 2, "cola2": "a2"]),
bs1: [],
bs2: [
B(row: ["colb1": 6, "colb2": 2, "colb3": "b3"]),
]),
])
}
// Record.fetchOne
do {
let record = try Record.fetchOne(db, request)!
XCTAssertEqual(record, Record(
a: A(row: ["cola1": 1, "cola2": "a1"]),
bs1: [
B(row: ["colb1": 4, "colb2": 1, "colb3": "b1"]),
],
bs2: [
B(row: ["colb1": 5, "colb2": 1, "colb3": "b2"]),
]))
}
}
}
}
func testIncludingAllHasManyIncludingAllHasMany() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.read { db in
// Plain request
do {
let request = A
.including(all: A
.hasMany(C.self)
.including(all: C
.hasMany(D.self)
.orderByPrimaryKey())
.orderByPrimaryKey())
.orderByPrimaryKey()
struct Record: FetchableRecord, Equatable {
struct CInfo: FetchableRecord, Equatable {
var c: C
var ds: [D]
init(c: C, ds: [D]) {
self.c = c
self.ds = ds
}
init(row: Row) {
self.init(c: C(row: row), ds: row["ds"])
}
}
var a: A
var cs: [CInfo]
init(a: A, cs: [CInfo]) {
self.a = a
self.cs = cs
}
init(row: Row) {
self.init(a: A(row: row), cs: row["cs"])
}
}
// Record.fetchAll
do {
let records = try Record.fetchAll(db, request)
XCTAssertEqual(records, [
Record(
a: A(row: ["cola1": 1, "cola2": "a1"]),
cs: [
Record.CInfo(
c: C(row: ["colc1": 7, "colc2": 1]),
ds: [
D(row: ["cold1": 10, "cold2": 7, "cold3": "d1"]),
]),
]),
Record(
a: A(row: ["cola1": 2, "cola2": "a2"]),
cs: [
Record.CInfo(
c: C(row: ["colc1": 8, "colc2": 2]),
ds: [
D(row: ["cold1": 11, "cold2": 8, "cold3": "d2"]),
D(row: ["cold1": 12, "cold2": 8, "cold3": "d3"]),
]),
Record.CInfo(
c: C(row: ["colc1": 9, "colc2": 2]),
ds: [
D(row: ["cold1": 13, "cold2": 9, "cold3": "d4"]),
]),
]),
Record(
a: A(row: ["cola1": 3, "cola2": "a3"]),
cs: []),
])
}
// Record.fetchOne
do {
let record = try Record.fetchOne(db, request)!
XCTAssertEqual(record, Record(
a: A(row: ["cola1": 1, "cola2": "a1"]),
cs: [
Record.CInfo(
c: C(row: ["colc1": 7, "colc2": 1]),
ds: [
D(row: ["cold1": 10, "cold2": 7, "cold3": "d1"]),
]),
]))
}
}
// Request with avoided prefetch
do {
let request = A
.including(all: A
.hasMany(C.self)
.none()
.including(all: C
.hasMany(D.self)
.orderByPrimaryKey())
.orderByPrimaryKey())
.orderByPrimaryKey()
struct Record: FetchableRecord, Equatable {
struct CInfo: FetchableRecord, Equatable {
var c: C
var ds: [D]
init(c: C, ds: [D]) {
self.c = c
self.ds = ds
}
init(row: Row) {
self.init(c: C(row: row), ds: row["ds"])
}
}
var a: A
var cs: [CInfo]
init(a: A, cs: [CInfo]) {
self.a = a
self.cs = cs
}
init(row: Row) {
self.init(a: A(row: row), cs: row["cs"])
}
}
// Record.fetchAll
do {
let records = try Record.fetchAll(db, request)
XCTAssertEqual(records, [
Record(
a: A(row: ["cola1": 1, "cola2": "a1"]),
cs: []),
Record(
a: A(row: ["cola1": 2, "cola2": "a2"]),
cs: []),
Record(
a: A(row: ["cola1": 3, "cola2": "a3"]),
cs: []),
])
}
// Record.fetchOne
do {
let record = try Record.fetchOne(db, request)!
XCTAssertEqual(record, Record(
a: A(row: ["cola1": 1, "cola2": "a1"]),
cs: []))
}
}
// Request with filters
do {
let request = A
.filter(Column("cola1") != 3)
.including(all: A
.hasMany(C.self)
.filter(Column("colc1") > 7)
.including(all: C
.hasMany(D.self)
.filter(Column("cold1") == 11)
.orderByPrimaryKey()
.forKey("ds1"))
.including(all: C
.hasMany(D.self)
.filter(Column("cold1") != 11)
.orderByPrimaryKey()
.forKey("ds2"))
.orderByPrimaryKey()
.forKey("cs1"))
.including(all: A
.hasMany(C.self)
.filter(Column("colc1") < 9)
.including(all: C
.hasMany(D.self)
.filter(Column("cold1") == 11)
.orderByPrimaryKey()
.forKey("ds1"))
.including(all: C
.hasMany(D.self)
.filter(Column("cold1") != 11)
.orderByPrimaryKey()
.forKey("ds2"))
.orderByPrimaryKey()
.forKey("cs2"))
.orderByPrimaryKey()
struct Record: FetchableRecord, Equatable {
struct CInfo: FetchableRecord, Equatable {
var c: C
var ds1: [D]
var ds2: [D]
init(c: C, ds1: [D], ds2: [D]) {
self.c = c
self.ds1 = ds1
self.ds2 = ds2
}
init(row: Row) {
self.init(c: C(row: row), ds1: row["ds1"], ds2: row["ds2"])
}
}
var a: A
var cs1: [CInfo]
var cs2: [CInfo]
init(a: A, cs1: [CInfo], cs2: [CInfo]) {
self.a = a
self.cs1 = cs1
self.cs2 = cs2
}
init(row: Row) {
self.init(a: A(row: row), cs1: row["cs1"], cs2: row["cs2"])
}
}
// Record.fetchAll
do {
let records = try Record.fetchAll(db, request)
XCTAssertEqual(records, [
Record(
a: A(row: ["cola1": 1, "cola2": "a1"]),
cs1: [],
cs2: [
Record.CInfo(
c: C(row: ["colc1": 7, "colc2": 1]),
ds1: [],
ds2: [
D(row: ["cold1": 10, "cold2": 7, "cold3": "d1"]),
]),
]),
Record(
a: A(row: ["cola1": 2, "cola2": "a2"]),
cs1: [
Record.CInfo(
c: C(row: ["colc1": 8, "colc2": 2]),
ds1: [
D(row: ["cold1": 11, "cold2": 8, "cold3": "d2"]),
],
ds2: [
D(row: ["cold1": 12, "cold2": 8, "cold3": "d3"]),
]),
Record.CInfo(
c: C(row: ["colc1": 9, "colc2": 2]),
ds1: [],
ds2: [
D(row: ["cold1": 13, "cold2": 9, "cold3": "d4"]),
]),
],
cs2: [
Record.CInfo(
c: C(row: ["colc1": 8, "colc2": 2]),
ds1: [
D(row: ["cold1": 11, "cold2": 8, "cold3": "d2"]),
],
ds2: [
D(row: ["cold1": 12, "cold2": 8, "cold3": "d3"]),
]),
]),
])
}
// Record.fetchOne
do {
let record = try Record.fetchOne(db, request)!
XCTAssertEqual(record, Record(
a: A(row: ["cola1": 1, "cola2": "a1"]),
cs1: [],
cs2: [
Record.CInfo(
c: C(row: ["colc1": 7, "colc2": 1]),
ds1: [],
ds2: [
D(row: ["cold1": 10, "cold2": 7, "cold3": "d1"]),
]),
]))
}
}
}
}
func testIncludingAllHasManyIncludingRequiredOrOptionalHasMany() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.read { db in
// Plain request
do {
let request = A
.including(all: A
.hasMany(C.self)
.including(required: C
.hasMany(D.self)
.orderByPrimaryKey())
.orderByPrimaryKey())
.orderByPrimaryKey()
struct Record: FetchableRecord, Equatable {
struct CInfo: FetchableRecord, Equatable {
var c: C
var d: D
init(c: C, d: D) {
self.c = c
self.d = d
}
init(row: Row) {
self.init(c: C(row: row), d: row["d"])
}
}
var a: A
var cs: [CInfo]
init(a: A, cs: [CInfo]) {
self.a = a
self.cs = cs
}
init(row: Row) {
self.init(a: A(row: row), cs: row["cs"])
}
}
// Record.fetchAll
do {
let records = try Record.fetchAll(db, request)
XCTAssertEqual(records, [
Record(
a: A(row: ["cola1": 1, "cola2": "a1"]),
cs: [
Record.CInfo(
c: C(row: ["colc1": 7, "colc2": 1]),
d: D(row: ["cold1": 10, "cold2": 7, "cold3": "d1"])),
]),
Record(
a: A(row: ["cola1": 2, "cola2": "a2"]),
cs: [
Record.CInfo(
c: C(row: ["colc1": 8, "colc2": 2]),
d: D(row: ["cold1": 11, "cold2": 8, "cold3": "d2"])),
Record.CInfo(
c: C(row: ["colc1": 8, "colc2": 2]),
d: D(row: ["cold1": 12, "cold2": 8, "cold3": "d3"])),
Record.CInfo(
c: C(row: ["colc1": 9, "colc2": 2]),
d: D(row: ["cold1": 13, "cold2": 9, "cold3": "d4"])),
]),
Record(
a: A(row: ["cola1": 3, "cola2": "a3"]),
cs: []),
])
}
// Record.fetchOne
do {
let record = try Record.fetchOne(db, request)!
XCTAssertEqual(record, Record(
a: A(row: ["cola1": 1, "cola2": "a1"]),
cs: [
Record.CInfo(
c: C(row: ["colc1": 7, "colc2": 1]),
d: D(row: ["cold1": 10, "cold2": 7, "cold3": "d1"])),
]))
}
}
// Request with avoided prefetch
do {
let request = A
.including(all: A
.hasMany(C.self)
.none()
.including(required: C
.hasMany(D.self)
.orderByPrimaryKey())
.orderByPrimaryKey())
.orderByPrimaryKey()
struct Record: FetchableRecord, Equatable {
struct CInfo: FetchableRecord, Equatable {
var c: C
var d: D
init(c: C, d: D) {
self.c = c
self.d = d
}
init(row: Row) {
self.init(c: C(row: row), d: row["d"])
}
}
var a: A
var cs: [CInfo]
init(a: A, cs: [CInfo]) {
self.a = a
self.cs = cs
}
init(row: Row) {
self.init(a: A(row: row), cs: row["cs"])
}
}
// Record.fetchAll
do {
let records = try Record.fetchAll(db, request)
XCTAssertEqual(records, [
Record(
a: A(row: ["cola1": 1, "cola2": "a1"]),
cs: []),
Record(
a: A(row: ["cola1": 2, "cola2": "a2"]),
cs: []),
Record(
a: A(row: ["cola1": 3, "cola2": "a3"]),
cs: []),
])
}
// Record.fetchOne
do {
let record = try Record.fetchOne(db, request)!
XCTAssertEqual(record, Record(
a: A(row: ["cola1": 1, "cola2": "a1"]),
cs: []))
}
}
// Request with filters
do {
let request = A
.filter(Column("cola1") != 3)
.including(all: A
.hasMany(C.self)
.filter(Column("colc1") > 7)
.including(optional: C
.hasMany(D.self)
.filter(Column("cold1") == 11)
.orderByPrimaryKey()
.forKey("d1"))
.including(required: C
.hasMany(D.self)
.filter(Column("cold1") != 11)
.orderByPrimaryKey()
.forKey("d2"))
.orderByPrimaryKey()
.forKey("cs1"))
.including(all: A
.hasMany(C.self)
.filter(Column("colc1") < 9)
.including(optional: C
.hasMany(D.self)
.filter(Column("cold1") == 11)
.orderByPrimaryKey()
.forKey("d1"))
.including(required: C
.hasMany(D.self)
.filter(Column("cold1") != 11)
.orderByPrimaryKey()
.forKey("d2"))
.orderByPrimaryKey()
.forKey("cs2"))
.orderByPrimaryKey()
struct Record: FetchableRecord, Equatable {
struct CInfo: FetchableRecord, Equatable {
var c: C
var d1: D?
var d2: D
init(c: C, d1: D?, d2: D) {
self.c = c
self.d1 = d1
self.d2 = d2
}
init(row: Row) {
self.init(c: C(row: row), d1: row["d1"], d2: row["d2"])
}
}
var a: A
var cs1: [CInfo]
var cs2: [CInfo]
init(a: A, cs1: [CInfo], cs2: [CInfo]) {
self.a = a
self.cs1 = cs1
self.cs2 = cs2
}
init(row: Row) {
self.init(a: A(row: row), cs1: row["cs1"], cs2: row["cs2"])
}
}
// Record.fetchAll
do {
let records = try Record.fetchAll(db, request)
XCTAssertEqual(records, [
Record(
a: A(row: ["cola1": 1, "cola2": "a1"]),
cs1: [],
cs2: [
Record.CInfo(
c: C(row: ["colc1": 7, "colc2": 1]),
d1: nil,
d2: D(row: ["cold1": 10, "cold2": 7, "cold3": "d1"])),
]),
Record(
a: A(row: ["cola1": 2, "cola2": "a2"]),
cs1: [
Record.CInfo(
c: C(row: ["colc1": 8, "colc2": 2]),
d1: D(row: ["cold1": 11, "cold2": 8, "cold3": "d2"]),
d2: D(row: ["cold1": 12, "cold2": 8, "cold3": "d3"])),
Record.CInfo(
c: C(row: ["colc1": 9, "colc2": 2]),
d1: nil,
d2: D(row: ["cold1": 13, "cold2": 9, "cold3": "d4"])),
],
cs2: [
Record.CInfo(
c: C(row: ["colc1": 8, "colc2": 2]),
d1: D(row: ["cold1": 11, "cold2": 8, "cold3": "d2"]),
d2: D(row: ["cold1": 12, "cold2": 8, "cold3": "d3"])),
]),
])
}
// Record.fetchOne
do {
let record = try Record.fetchOne(db, request)!
XCTAssertEqual(record, Record(
a: A(row: ["cola1": 1, "cola2": "a1"]),
cs1: [],
cs2: [
Record.CInfo(
c: C(row: ["colc1": 7, "colc2": 1]),
d1: nil,
d2: D(row: ["cold1": 10, "cold2": 7, "cold3": "d1"])),
]))
}
}
}
}
func testIncludingAllHasManyThroughHasManyUsingHasMany() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.read { db in
// Plain request
do {
let request = A
.including(all: A
.hasMany(D.self, through: A.hasMany(C.self), using: C.hasMany(D.self))
.orderByPrimaryKey())
.orderByPrimaryKey()
struct Record: FetchableRecord, Equatable {
var a: A
var ds: [D]
init(a: A, ds: [D]) {
self.a = a
self.ds = ds
}
init(row: Row) {
self.init(a: A(row: row), ds: row["ds"])
}
}
// Record.fetchAll
do {
let records = try Record.fetchAll(db, request)
XCTAssertEqual(records, [
Record(
a: A(row: ["cola1": 1, "cola2": "a1"]),
ds: [
D(row: ["cold1": 10, "cold2": 7, "cold3": "d1"]),
]),
Record(
a: A(row: ["cola1": 2, "cola2": "a2"]),
ds: [
D(row: ["cold1": 11, "cold2": 8, "cold3": "d2"]),
D(row: ["cold1": 12, "cold2": 8, "cold3": "d3"]),
D(row: ["cold1": 13, "cold2": 9, "cold3": "d4"]),
]),
Record(
a: A(row: ["cola1": 3, "cola2": "a3"]),
ds: []),
])
}
// Record.fetchOne
do {
let record = try Record.fetchOne(db, request)!
XCTAssertEqual(record, Record(
a: A(row: ["cola1": 1, "cola2": "a1"]),
ds: [
D(row: ["cold1": 10, "cold2": 7, "cold3": "d1"]),
]))
}
}
// Request with filters
do {
let request = A
.filter(Column("cola1") != 3)
.including(all: A
.hasMany(D.self, through: A.hasMany(C.self).filter(Column("colc1") == 8).forKey("cs1"), using: C.hasMany(D.self))
.orderByPrimaryKey()
.forKey("ds1"))
.including(all: A
.hasMany(D.self, through: A.hasMany(C.self).forKey("cs2"), using: C.hasMany(D.self))
.filter(Column("cold1") != 11)
.orderByPrimaryKey()
.forKey("ds2"))
.including(all: A
.hasMany(D.self, through: A.hasMany(C.self).forKey("cs2"), using: C.hasMany(D.self))
.filter(Column("cold1") == 11)
.orderByPrimaryKey()
.forKey("ds3"))
.orderByPrimaryKey()
struct Record: FetchableRecord, Equatable {
var a: A
var ds1: [D]
var ds2: [D]
var ds3: [D]
init(a: A, ds1: [D], ds2: [D], ds3: [D]) {
self.a = a
self.ds1 = ds1
self.ds2 = ds2
self.ds3 = ds3
}
init(row: Row) {
self.init(a: A(row: row), ds1: row["ds1"], ds2: row["ds2"], ds3: row["ds3"])
}
}
// Record.fetchAll
do {
let records = try Record.fetchAll(db, request)
XCTAssertEqual(records, [
Record(
a: A(row: ["cola1": 1, "cola2": "a1"]),
ds1: [],
ds2: [
D(row: ["cold1": 10, "cold2": 7, "cold3": "d1"]),
],
ds3: []),
Record(
a: A(row: ["cola1": 2, "cola2": "a2"]),
ds1: [
D(row: ["cold1": 11, "cold2": 8, "cold3": "d2"]),
D(row: ["cold1": 12, "cold2": 8, "cold3": "d3"]),
],
ds2: [
D(row: ["cold1": 12, "cold2": 8, "cold3": "d3"]),
D(row: ["cold1": 13, "cold2": 9, "cold3": "d4"]),
],
ds3: [
D(row: ["cold1": 11, "cold2": 8, "cold3": "d2"]),
]),
])
}
// Record.fetchOne
do {
let record = try Record.fetchOne(db, request)!
XCTAssertEqual(record, Record(
a: A(row: ["cola1": 1, "cola2": "a1"]),
ds1: [],
ds2: [
D(row: ["cold1": 10, "cold2": 7, "cold3": "d1"]),
],
ds3: []))
}
}
}
}
func testIncludingOptionalBelongsToIncludingAllHasMany() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.write { db in
// Plain request
do {
let request = B
.including(optional: B
.belongsTo(A.self)
.including(all: A
.hasMany(C.self)
.orderByPrimaryKey())
)
.orderByPrimaryKey()
// Record (nested)
do {
struct Record: FetchableRecord, Equatable {
struct AInfo: FetchableRecord, Equatable {
var a: A
var cs: [C]
init(a: A, cs: [C]) {
self.a = a
self.cs = cs
}
init(row: Row) {
self.init(a: A(row: row), cs: row["cs"])
}
}
var b: B
var a: AInfo?
init(b: B, a: AInfo?) {
self.b = b
self.a = a
}
init(row: Row) {
self.init(b: B(row: row), a: row["a"])
}
}
// Record.fetchAll
do {
let records = try Record.fetchAll(db, request)
XCTAssertEqual(records, [
Record(
b: B(row: ["colb1": 4, "colb2": 1, "colb3": "b1"]),
a: Record.AInfo(
a: A(row: ["cola1": 1, "cola2": "a1"]),
cs: [
C(row: ["colc1": 7, "colc2": 1]),
])),
Record(
b: B(row: ["colb1": 5, "colb2": 1, "colb3": "b2"]),
a: Record.AInfo(
a: A(row: ["cola1": 1, "cola2": "a1"]),
cs: [
C(row: ["colc1": 7, "colc2": 1]),
])),
Record(
b: B(row: ["colb1": 6, "colb2": 2, "colb3": "b3"]),
a: Record.AInfo(
a: A(row: ["cola1": 2, "cola2": "a2"]),
cs: [
C(row: ["colc1": 8, "colc2": 2]),
C(row: ["colc1": 9, "colc2": 2]),
])),
Record(
b: B(row: ["colb1": 14, "colb2": nil, "colb3": "b4"]),
a: nil),
])
}
// Record.fetchOne
do {
let record = try Record.fetchOne(db, request)!
XCTAssertEqual(record, Record(
b: B(row: ["colb1": 4, "colb2": 1, "colb3": "b1"]),
a: Record.AInfo(
a: A(row: ["cola1": 1, "cola2": "a1"]),
cs: [
C(row: ["colc1": 7, "colc2": 1]),
])))
}
}
// Record (flat)
do {
struct Record: FetchableRecord, Equatable {
var b: B
var a: A?
var cs: [C] // not optional
init(b: B, a: A?, cs: [C]) {
self.b = b
self.a = a
self.cs = cs
}
init(row: Row) {
self.init(b: B(row: row), a: row["a"], cs: row["cs"])
}
}
// Record.fetchAll
do {
let records = try Record.fetchAll(db, request)
XCTAssertEqual(records, [
Record(
b: B(row: ["colb1": 4, "colb2": 1, "colb3": "b1"]),
a: A(row: ["cola1": 1, "cola2": "a1"]),
cs: [
C(row: ["colc1": 7, "colc2": 1]),
]),
Record(
b: B(row: ["colb1": 5, "colb2": 1, "colb3": "b2"]),
a: A(row: ["cola1": 1, "cola2": "a1"]),
cs: [
C(row: ["colc1": 7, "colc2": 1]),
]),
Record(
b: B(row: ["colb1": 6, "colb2": 2, "colb3": "b3"]),
a: A(row: ["cola1": 2, "cola2": "a2"]),
cs: [
C(row: ["colc1": 8, "colc2": 2]),
C(row: ["colc1": 9, "colc2": 2]),
]),
Record(
b: B(row: ["colb1": 14, "colb2": nil, "colb3": "b4"]),
a: nil,
cs: []),
])
}
// Record.fetchOne
do {
let record = try Record.fetchOne(db, request)!
XCTAssertEqual(record, Record(
b: B(row: ["colb1": 4, "colb2": 1, "colb3": "b1"]),
a: A(row: ["cola1": 1, "cola2": "a1"]),
cs: [
C(row: ["colc1": 7, "colc2": 1]),
]))
}
}
}
// Request with filters
do {
let request = B
.including(optional: B
.belongsTo(A.self)
.filter(Column("cola2") == "a1")
.including(all: A
.hasMany(C.self)
.filter(Column("colc1") == 9)
.orderByPrimaryKey()
.forKey("cs1"))
.including(all: A
.hasMany(C.self)
.filter(Column("colc1") != 9)
.orderByPrimaryKey()
.forKey("cs2"))
.forKey("a1"))
.including(optional: B
.belongsTo(A.self)
.filter(Column("cola2") == "a2")
.including(all: A
.hasMany(C.self)
.filter(Column("colc1") == 9)
.orderByPrimaryKey()
.forKey("cs1"))
.including(all: A
.hasMany(C.self)
.filter(Column("colc1") != 9)
.orderByPrimaryKey()
.forKey("cs2"))
.forKey("a2"))
.orderByPrimaryKey()
struct Record: FetchableRecord, Equatable {
struct AInfo: FetchableRecord, Equatable {
var a: A
var cs1: [C]
var cs2: [C]
init(a: A, cs1: [C], cs2: [C]) {
self.a = a
self.cs1 = cs1
self.cs2 = cs2
}
init(row: Row) {
self.init(a: A(row: row), cs1: row["cs1"], cs2: row["cs2"])
}
}
var b: B
var a1: AInfo?
var a2: AInfo?
init(b: B, a1: AInfo?, a2: AInfo?) {
self.b = b
self.a1 = a1
self.a2 = a2
}
init(row: Row) {
self.init(b: B(row: row), a1: row["a1"], a2: row["a2"])
}
}
// Record.fetchAll
do {
let records = try Record.fetchAll(db, request)
XCTAssertEqual(records, [
Record(
b: B(row: ["colb1": 4, "colb2": 1, "colb3": "b1"]),
a1: Record.AInfo(
a: A(row: ["cola1": 1, "cola2": "a1"]),
cs1: [],
cs2: [
C(row: ["colc1": 7, "colc2": 1]),
]),
a2: nil),
Record(
b: B(row: ["colb1": 5, "colb2": 1, "colb3": "b2"]),
a1: Record.AInfo(
a: A(row: ["cola1": 1, "cola2": "a1"]),
cs1: [],
cs2: [
C(row: ["colc1": 7, "colc2": 1]),
]),
a2: nil),
Record(
b: B(row: ["colb1": 6, "colb2": 2, "colb3": "b3"]),
a1: nil,
a2: Record.AInfo(
a: A(row: ["cola1": 2, "cola2": "a2"]),
cs1: [
C(row: ["colc1": 9, "colc2": 2]),
],
cs2: [
C(row: ["colc1": 8, "colc2": 2]),
])),
Record(
b: B(row: ["colb1": 14, "colb2": nil, "colb3": "b4"]),
a1: nil,
a2: nil),
])
}
// Record.fetchOne
do {
let record = try Record.fetchOne(db, request)!
XCTAssertEqual(record, Record(
b: B(row: ["colb1": 4, "colb2": 1, "colb3": "b1"]),
a1: Record.AInfo(
a: A(row: ["cola1": 1, "cola2": "a1"]),
cs1: [],
cs2: [
C(row: ["colc1": 7, "colc2": 1]),
]),
a2: nil))
}
}
}
}
func testJoiningOptionalHasOneThroughIncludingAllHasMany() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.write { db in
// Plain request
do {
let request = D
.joining(optional: D
.hasOne(A.self, through: D.belongsTo(C.self), using: C.belongsTo(A.self))
.including(all: A
.hasMany(B.self)
.orderByPrimaryKey()))
.orderByPrimaryKey()
// Record (flat)
do {
struct Record: FetchableRecord, Equatable {
var d: D
var bs: [B] // not optional
init(d: D, bs: [B]) {
self.d = d
self.bs = bs
}
init(row: Row) {
self.init(d: D(row: row), bs: row["bs"])
}
}
// Record.fetchAll
do {
let records = try Record.fetchAll(db, request)
XCTAssertEqual(records, [
Record(
d: D(row: ["cold1": 10, "cold2": 7, "cold3": "d1"]),
bs: [
B(row: ["colb1": 4, "colb2": 1, "colb3": "b1"]),
B(row: ["colb1": 5, "colb2": 1, "colb3": "b2"]),
]),
Record(
d: D(row: ["cold1": 11, "cold2": 8, "cold3": "d2"]),
bs: [
B(row: ["colb1": 6, "colb2": 2, "colb3": "b3"]),
]),
Record(
d: D(row: ["cold1": 12, "cold2": 8, "cold3": "d3"]),
bs: [
B(row: ["colb1": 6, "colb2": 2, "colb3": "b3"]),
]),
Record(
d: D(row: ["cold1": 13, "cold2": 9, "cold3": "d4"]),
bs: [
B(row: ["colb1": 6, "colb2": 2, "colb3": "b3"]),
]),
Record(
d: D(row: ["cold1": 14, "cold2": nil, "cold3": "d5"]),
bs: []),
])
}
// Record.fetchOne
do {
let record = try Record.fetchOne(db, request)!
XCTAssertEqual(record, Record(
d: D(row: ["cold1": 10, "cold2": 7, "cold3": "d1"]),
bs: [
B(row: ["colb1": 4, "colb2": 1, "colb3": "b1"]),
B(row: ["colb1": 5, "colb2": 1, "colb3": "b2"]),
]))
}
}
}
}
}
}