Skip to content

Commit 98a9a4c

Browse files
authored
Add join overloads for Joins: Table (pointfreeco#136)
* Add join overloads for `Joins: Table` Parameter packs break down when a pack generic is a single value (instead of a tuple) and has a conformance constraint. These overloads are to work around it. Before 1.0 we should probably come up with a reasonable limitation of how joins work. Some ideas to reduce the overloads: - Joins require no selected columns on either side. While it'd be nice to splat them, it just doesn't work well with parameter packs today. - Joins require no joined tables on the righthand side. Again: it'd be nice to support this, but it doesn't work well in the language today. This maybe even means joins take `Table.Type` instead of `Select<Table>`. I think if these two limitations were known and documented we could eliminate the many of the overloads we've defined today. * wip * fix
1 parent fb083bd commit 98a9a4c

File tree

2 files changed

+139
-0
lines changed

2 files changed

+139
-0
lines changed

Sources/StructuredQueriesCore/Statements/Select.swift

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,36 @@ extension Select {
710710
)
711711
}
712712

713+
@_disfavoredOverload
714+
@_documentation(visibility: private)
715+
public func join<F: Table>(
716+
// TODO: Report issue to Swift team. Using 'some' crashes the compiler.
717+
_ other: any SelectStatementOf<F>,
718+
on constraint: (
719+
(From.TableColumns, Joins.TableColumns, F.TableColumns)
720+
) -> some QueryExpression<Bool>
721+
) -> Select<(), From, (Joins, F)> where Joins: Table {
722+
let other = other.asSelect()
723+
let join = _JoinClause(
724+
operator: .inner,
725+
table: F.self,
726+
constraint: constraint(
727+
(From.columns, Joins.columns, F.columns)
728+
)
729+
)
730+
return Select<(), From, (Joins, F)>(
731+
isEmpty: isEmpty || other.isEmpty,
732+
distinct: distinct || other.distinct,
733+
columns: columns + other.columns,
734+
joins: joins + [join] + other.joins,
735+
where: `where` + other.where,
736+
group: group + other.group,
737+
having: having + other.having,
738+
order: order + other.order,
739+
limit: other.limit ?? limit
740+
)
741+
}
742+
713743
/// Creates a new select statement from this one by left-joining another table.
714744
///
715745
/// - Parameters:
@@ -849,6 +879,37 @@ extension Select {
849879
)
850880
}
851881

882+
@_disfavoredOverload
883+
@_documentation(visibility: private)
884+
public func leftJoin<F: Table>(
885+
// TODO: Report issue to Swift team. Using 'some' crashes the compiler.
886+
_ other: any SelectStatementOf<F>,
887+
on constraint: (
888+
(From.TableColumns, Joins.TableColumns, F.TableColumns)
889+
) -> some QueryExpression<Bool>
890+
) -> Select<(), From, (Joins, F._Optionalized)>
891+
where Joins: Table {
892+
let other = other.asSelect()
893+
let join = _JoinClause(
894+
operator: .left,
895+
table: F.self,
896+
constraint: constraint(
897+
(From.columns, Joins.columns, F.columns)
898+
)
899+
)
900+
return Select<(), From, (Joins, F._Optionalized)>(
901+
isEmpty: isEmpty || other.isEmpty,
902+
distinct: distinct || other.distinct,
903+
columns: columns + other.columns,
904+
joins: joins + [join] + other.joins,
905+
where: `where` + other.where,
906+
group: group + other.group,
907+
having: having + other.having,
908+
order: order + other.order,
909+
limit: other.limit ?? limit
910+
)
911+
}
912+
852913
/// Creates a new select statement from this one by right-joining another table.
853914
///
854915
/// - Parameters:
@@ -988,6 +1049,37 @@ extension Select {
9881049
)
9891050
}
9901051

1052+
@_disfavoredOverload
1053+
@_documentation(visibility: private)
1054+
public func rightJoin<F: Table>(
1055+
// TODO: Report issue to Swift team. Using 'some' crashes the compiler.
1056+
_ other: any SelectStatementOf<F>,
1057+
on constraint: (
1058+
(From.TableColumns, Joins.TableColumns, F.TableColumns)
1059+
) -> some QueryExpression<Bool>
1060+
) -> Select<(), From._Optionalized, (Joins._Optionalized, F)>
1061+
where Joins: Table {
1062+
let other = other.asSelect()
1063+
let join = _JoinClause(
1064+
operator: .right,
1065+
table: F.self,
1066+
constraint: constraint(
1067+
(From.columns, Joins.columns, F.columns)
1068+
)
1069+
)
1070+
return Select<(), From._Optionalized, (Joins._Optionalized, F)>(
1071+
isEmpty: isEmpty || other.isEmpty,
1072+
distinct: distinct || other.distinct,
1073+
columns: columns + other.columns,
1074+
joins: joins + [join] + other.joins,
1075+
where: `where` + other.where,
1076+
group: group + other.group,
1077+
having: having + other.having,
1078+
order: order + other.order,
1079+
limit: other.limit ?? limit
1080+
)
1081+
}
1082+
9911083
/// Creates a new select statement from this one by full-joining another table.
9921084
///
9931085
/// - Parameters:
@@ -1127,6 +1219,37 @@ extension Select {
11271219
)
11281220
}
11291221

1222+
@_disfavoredOverload
1223+
@_documentation(visibility: private)
1224+
public func fullJoin<F: Table>(
1225+
// TODO: Report issue to Swift team. Using 'some' crashes the compiler.
1226+
_ other: any SelectStatementOf<F>,
1227+
on constraint: (
1228+
(From.TableColumns, Joins.TableColumns, F.TableColumns)
1229+
) -> some QueryExpression<Bool>
1230+
) -> Select<(), From._Optionalized, (Joins._Optionalized, F._Optionalized)>
1231+
where Joins: Table {
1232+
let other = other.asSelect()
1233+
let join = _JoinClause(
1234+
operator: .full,
1235+
table: F.self,
1236+
constraint: constraint(
1237+
(From.columns, Joins.columns, F.columns)
1238+
)
1239+
)
1240+
return Select<(), From._Optionalized, (Joins._Optionalized, F._Optionalized)>(
1241+
isEmpty: isEmpty || other.isEmpty,
1242+
distinct: distinct || other.distinct,
1243+
columns: columns + other.columns,
1244+
joins: joins + [join] + other.joins,
1245+
where: `where` + other.where,
1246+
group: group + other.group,
1247+
having: having + other.having,
1248+
order: order + other.order,
1249+
limit: other.limit ?? limit
1250+
)
1251+
}
1252+
11301253
/// Creates a new select statement from this one by appending a predicate to its `WHERE` clause.
11311254
///
11321255
/// - Parameter keyPath: A key path from this select's table to a Boolean expression to filter by.

Tests/StructuredQueriesTests/SelectTests.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,6 +1406,22 @@ extension SnapshotTests {
14061406
@Test func singleJoinChaining() {
14071407
let base = Reminder.group(by: \.id).join(ReminderTag.all) { $0.id.eq($1.reminderID) }
14081408
_ = base.select { r, _ in r.isCompleted }
1409+
_ = base.join(RemindersList.all) { _, _, _ in true }
1410+
_ = base.leftJoin(RemindersList.all) { _, _, _ in true }
1411+
_ = base.rightJoin(RemindersList.all) { _, _, _ in true }
1412+
_ = base.fullJoin(RemindersList.all) { _, _, _ in true }
1413+
_ = base
1414+
.join(RemindersList.all) { _, _, _ in true }
1415+
.join(RemindersList.all) { _, _, _, _ in true }
1416+
_ = base
1417+
.leftJoin(RemindersList.all) { _, _, _ in true }
1418+
.leftJoin(RemindersList.all) { _, _, _, _ in true }
1419+
_ = base
1420+
.rightJoin(RemindersList.all) { _, _, _ in true }
1421+
.rightJoin(RemindersList.all) { _, _, _, _ in true }
1422+
_ = base
1423+
.fullJoin(RemindersList.all) { _, _, _ in true }
1424+
.fullJoin(RemindersList.all) { _, _, _, _ in true }
14091425
_ = base.where { r, _ in r.isCompleted }
14101426
_ = base.group { r, _ in r.isCompleted }
14111427
_ = base.having { r, _ in r.isCompleted }

0 commit comments

Comments
 (0)