Skip to content

Commit c73fb0d

Browse files
committed
Back to basics
1 parent c966eb3 commit c73fb0d

File tree

3 files changed

+231
-452
lines changed

3 files changed

+231
-452
lines changed

GRDB/QueryInterface/SQLGeneration/SQLQueryGenerator.swift

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,18 @@ private struct SQLQualifiedJoin: Refinable {
611611
enum Kind: String {
612612
case leftJoin = "LEFT JOIN"
613613
case innerJoin = "JOIN"
614+
615+
init?(_ kind: SQLRelation.Child.Kind) {
616+
switch kind {
617+
case .oneRequired:
618+
self = .innerJoin
619+
case .oneOptional:
620+
self = .leftJoin
621+
case .allPrefetched, .allNotPrefetched:
622+
// This relation child is not fetched with an SQL join.
623+
return nil
624+
}
625+
}
614626
}
615627

616628
enum Target {
@@ -624,42 +636,44 @@ private struct SQLQualifiedJoin: Refinable {
624636
var target: Target
625637

626638
init?(_ child: SQLRelation.Child) {
627-
switch child.kind {
628-
case .oneRequired:
629-
kind = .innerJoin
630-
case .oneOptional:
631-
kind = .leftJoin
632-
case .allPrefetched, .allNotPrefetched:
633-
// This relation child is not fetched with an SQL join.
639+
guard let kind = Kind(child.kind) else {
634640
return nil
635641
}
636-
637-
condition = child.condition
638-
639-
if child.relation.firstOnly {
640-
// Filters and order are handled in a subRelation.
641-
// We only keep children that need to be exposed in the outer
642-
// query (those with a non-empty selection).
643-
#warning("TODO outerRelation 1")
644-
// TODO: we may have a problem if we remove children used for
645-
// ordering or filtering with aliases.
646-
#warning("TODO outerRelation 2")
647-
// TODO: keep children that have children with selection
648-
#warning("TODO target")
649-
// TODO: in the target remove to-many children
650-
let outerRelation = child.relation
642+
self.kind = kind
643+
self.condition = child.condition
644+
645+
// Remove non-joined grand children
646+
let relation = child.relation.filteringChildren { Kind($0.kind) != nil }
647+
648+
if relation.firstOnly {
649+
// Split relation into an "outer" relation exposed to the rest of
650+
// the query, and a "subRelation", private to the join, limited
651+
// to a single row:
652+
//
653+
// v--- outer relation
654+
// JOIN child ON id = (SELECT id FROM child ... LIMIT 1)
655+
// ^--- subRelation
656+
//
657+
// Outer relation has no filter or ordering: those are handled
658+
// by the subRelation.
659+
660+
guard relation.children.isEmpty else {
661+
// TODO: handle children
662+
fatalError("Not implemented: associating records to a `first` or `last` to-one association")
663+
}
664+
665+
let outerRelation = relation
651666
.with(\.firstOnly, false)
652667
.unfiltered()
653668
.unordered()
654-
.filteringChildren { $0.relation.selection.isEmpty == false }
655669

656-
let subRelation = child.relation
670+
let subRelation = relation
657671

658-
relation = SQLQualifiedRelation(outerRelation)
659-
target = .subRelation(subRelation)
672+
self.relation = SQLQualifiedRelation(outerRelation)
673+
self.target = .subRelation(subRelation)
660674
} else {
661-
relation = SQLQualifiedRelation(child.relation)
662-
target = .all
675+
self.relation = SQLQualifiedRelation(child.relation)
676+
self.target = .all
663677
}
664678
}
665679

0 commit comments

Comments
 (0)