@@ -611,6 +611,18 @@ private struct SQLQualifiedJoin: Refinable {
611
611
enum Kind : String {
612
612
case leftJoin = " LEFT JOIN "
613
613
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
+ }
614
626
}
615
627
616
628
enum Target {
@@ -624,42 +636,44 @@ private struct SQLQualifiedJoin: Refinable {
624
636
var target : Target
625
637
626
638
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 {
634
640
return nil
635
641
}
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
651
666
. with ( \. firstOnly, false )
652
667
. unfiltered ( )
653
668
. unordered ( )
654
- . filteringChildren { $0. relation. selection. isEmpty == false }
655
669
656
- let subRelation = child . relation
670
+ let subRelation = relation
657
671
658
- relation = SQLQualifiedRelation ( outerRelation)
659
- target = . subRelation( subRelation)
672
+ self . relation = SQLQualifiedRelation ( outerRelation)
673
+ self . target = . subRelation( subRelation)
660
674
} else {
661
- relation = SQLQualifiedRelation ( child. relation)
662
- target = . all
675
+ self . relation = SQLQualifiedRelation ( child. relation)
676
+ self . target = . all
663
677
}
664
678
}
665
679
0 commit comments