Skip to content

Commit 60d6b4f

Browse files
committed
Better support for optionals in queries
* Predicates (`where` and `having`) can return optional booleans. * Add `QueryExpression<Optional>.map` for optionally building queries on non-optional values. For example, a comparison that might use the `#sql` macro as an escape hatch can more safely and succinctly use `map`: ```diff Reminder.where { - #sql("\($0.dueDate) < \(Date())") + $0.dueDate.map { $0 < Date() } } ```
1 parent 995df0b commit 60d6b4f

File tree

7 files changed

+38
-14
lines changed

7 files changed

+38
-14
lines changed

Sources/StructuredQueriesCore/Optional.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,11 @@ where Wrapped.TableColumns: PrimaryKeyedTableDefinition {
133133
self[dynamicMember: \.primaryKey]
134134
}
135135
}
136+
137+
extension QueryExpression where QueryValue: _OptionalProtocol {
138+
public func map<T>(
139+
_ transform: (SQLQueryExpression<QueryValue.Wrapped>) -> some QueryExpression<T>
140+
) -> some QueryExpression<T?> {
141+
SQLQueryExpression(transform(SQLQueryExpression(queryFragment)).queryFragment)
142+
}
143+
}

Sources/StructuredQueriesCore/QueryFragmentBuilder.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ extension QueryFragmentBuilder<Bool> {
2727
) -> [QueryFragment] {
2828
[expression.queryFragment]
2929
}
30+
31+
public static func buildExpression(
32+
_ expression: some QueryExpression<some _OptionalPromotable<Bool?>>
33+
) -> [QueryFragment] {
34+
[expression.queryFragment]
35+
}
3036
}
3137

3238
extension QueryFragmentBuilder<()> {

Sources/StructuredQueriesCore/Statements/Delete.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ public struct Delete<From: Table, Returning> {
4848
///
4949
/// - Parameter keyPath: A key path to a Boolean expression to filter by.
5050
/// - Returns: A statement with the added predicate.
51-
public func `where`(_ keyPath: KeyPath<From.TableColumns, some QueryExpression<Bool>>) -> Self {
51+
public func `where`(
52+
_ keyPath: KeyPath<From.TableColumns, some QueryExpression<some _OptionalPromotable<Bool?>>>
53+
) -> Self {
5254
var update = self
5355
update.where.append(From.columns[keyPath: keyPath].queryFragment)
5456
return update
@@ -64,7 +66,9 @@ public struct Delete<From: Table, Returning> {
6466
/// - Parameter predicate: A closure that returns a Boolean expression to filter by.
6567
/// - Returns: A statement with the added predicate.
6668
@_disfavoredOverload
67-
public func `where`(_ predicate: (From.TableColumns) -> some QueryExpression<Bool>) -> Self {
69+
public func `where`(
70+
_ predicate: (From.TableColumns) -> some QueryExpression<some _OptionalPromotable<Bool?>>
71+
) -> Self {
6872
var update = self
6973
update.where.append(predicate(From.columns).queryFragment)
7074
return update

Sources/StructuredQueriesCore/Statements/Select.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ extension Table {
241241
/// columns.
242242
/// - Returns: A select statement that is filtered by the given predicate.
243243
public static func having(
244-
_ predicate: (TableColumns) -> some QueryExpression<Bool>
244+
_ predicate: (TableColumns) -> some QueryExpression<some _OptionalPromotable<Bool?>>
245245
) -> SelectOf<Self> {
246246
Where().having(predicate)
247247
}
@@ -1103,7 +1103,7 @@ extension Select {
11031103
/// - Parameter keyPath: A key path from this select's table to a Boolean expression to filter by.
11041104
/// - Returns: A new select statement that appends the given predicate to its `WHERE` clause.
11051105
public func `where`(
1106-
_ keyPath: KeyPath<From.TableColumns, some QueryExpression<Bool>>
1106+
_ keyPath: KeyPath<From.TableColumns, some QueryExpression<some _OptionalPromotable<Bool?>>>
11071107
) -> Self
11081108
where Joins == () {
11091109
var select = self
@@ -1118,7 +1118,9 @@ extension Select {
11181118
/// - Returns: A new select statement that appends the given predicate to its `WHERE` clause.
11191119
@_disfavoredOverload
11201120
public func `where`<each J: Table>(
1121-
_ predicate: (From.TableColumns, repeat (each J).TableColumns) -> some QueryExpression<Bool>
1121+
_ predicate: (From.TableColumns, repeat (each J).TableColumns) -> some QueryExpression<
1122+
some _OptionalPromotable<Bool?>
1123+
>
11221124
) -> Self
11231125
where Joins == (repeat each J) {
11241126
var select = self
@@ -1222,7 +1224,9 @@ extension Select {
12221224
/// - Returns: A new select statement that appends the given predicate to its `HAVING` clause.
12231225
@_disfavoredOverload
12241226
public func having<each J: Table>(
1225-
_ predicate: (From.TableColumns, repeat (each J).TableColumns) -> some QueryExpression<Bool>
1227+
_ predicate: (From.TableColumns, repeat (each J).TableColumns) -> some QueryExpression<
1228+
some _OptionalPromotable<Bool?>
1229+
>
12261230
) -> Self
12271231
where Joins == (repeat each J) {
12281232
var select = self

Sources/StructuredQueriesCore/Statements/SelectStatement.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public typealias SelectStatementOf<From: Table, each Join: Table> =
4747

4848
extension SelectStatement {
4949
public static func `where`<From>(
50-
_ predicate: (From.TableColumns) -> some QueryExpression<Bool>
50+
_ predicate: (From.TableColumns) -> some QueryExpression<some _OptionalPromotable<Bool?>>
5151
) -> Self
5252
where Self == Where<From> {
5353
Self(predicates: [predicate(From.columns).queryFragment])

Sources/StructuredQueriesCore/Statements/Update.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,9 @@ public struct Update<From: Table, Returning> {
105105
///
106106
/// - Parameter keyPath: A key path to a Boolean expression to filter by.
107107
/// - Returns: A statement with the added predicate.
108-
public func `where`(_ keyPath: KeyPath<From.TableColumns, some QueryExpression<Bool>>) -> Self {
108+
public func `where`(
109+
_ keyPath: KeyPath<From.TableColumns, some QueryExpression<some _OptionalPromotable<Bool?>>>
110+
) -> Self {
109111
var update = self
110112
update.where.append(From.columns[keyPath: keyPath].queryFragment)
111113
return update
@@ -117,7 +119,7 @@ public struct Update<From: Table, Returning> {
117119
/// - Returns: A statement with the added predicate.
118120
@_disfavoredOverload
119121
public func `where`(
120-
_ predicate: (From.TableColumns) -> some QueryExpression<Bool>
122+
_ predicate: (From.TableColumns) -> some QueryExpression<some _OptionalPromotable<Bool?>>
121123
) -> Self {
122124
var update = self
123125
update.where.append(predicate(From.columns).queryFragment)

Sources/StructuredQueriesCore/Statements/Where.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ extension Table {
2020
/// - Parameter keyPath: A key path to a Boolean expression to filter by.
2121
/// - Returns: A `WHERE` clause.
2222
public static func `where`(
23-
_ keyPath: KeyPath<TableColumns, some QueryExpression<Bool>>
23+
_ keyPath: KeyPath<TableColumns, some QueryExpression<some _OptionalPromotable<Bool?>>>
2424
) -> Where<Self> {
2525
Where(predicates: [columns[keyPath: keyPath].queryFragment])
2626
}
@@ -33,7 +33,7 @@ extension Table {
3333
/// - Returns: A `WHERE` clause.
3434
@_disfavoredOverload
3535
public static func `where`(
36-
_ predicate: (TableColumns) -> some QueryExpression<Bool>
36+
_ predicate: (TableColumns) -> some QueryExpression<some _OptionalPromotable<Bool?>>
3737
) -> Where<Self> {
3838
Where(predicates: [predicate(columns).queryFragment])
3939
}
@@ -285,7 +285,7 @@ extension Where: SelectStatement {
285285
/// - Parameter keyPath: A key path to a Boolean expression to filter by.
286286
/// - Returns: A where clause with the added predicate.
287287
public func `where`(
288-
_ keyPath: KeyPath<From.TableColumns, some QueryExpression<Bool>>
288+
_ keyPath: KeyPath<From.TableColumns, some QueryExpression<some _OptionalPromotable<Bool?>>>
289289
) -> Self {
290290
var `where` = self
291291
`where`.predicates.append(From.columns[keyPath: keyPath].queryFragment)
@@ -298,7 +298,7 @@ extension Where: SelectStatement {
298298
/// - Returns: A where clause with the added predicate.
299299
@_disfavoredOverload
300300
public func `where`(
301-
_ predicate: (From.TableColumns) -> some QueryExpression<Bool>
301+
_ predicate: (From.TableColumns) -> some QueryExpression<some _OptionalPromotable<Bool?>>
302302
) -> Self {
303303
var `where` = self
304304
`where`.predicates.append(predicate(From.columns).queryFragment)
@@ -408,7 +408,7 @@ extension Where: SelectStatement {
408408

409409
/// A select statement for the filtered table with the given `HAVING` clause.
410410
public func having(
411-
_ predicate: (From.TableColumns) -> some QueryExpression<Bool>
411+
_ predicate: (From.TableColumns) -> some QueryExpression<some _OptionalPromotable<Bool?>>
412412
) -> SelectOf<From> {
413413
asSelect().having(predicate)
414414
}

0 commit comments

Comments
 (0)