Skip to content

Commit d800a79

Browse files
authored
Merge pull request #1745 from groue/dev/multi-argument-max
Add the MIN and MAX multi-argument SQL functions to the query interface
2 parents 6eba24d + 4065ff5 commit d800a79

File tree

3 files changed

+75
-19
lines changed

3 files changed

+75
-19
lines changed

GRDB/QueryInterface/SQL/SQLExpression.swift

+2
Original file line numberDiff line numberDiff line change
@@ -2209,8 +2209,10 @@ extension SQLExpressible where Self == Column {
22092209
/// - ``localizedLowercased``
22102210
/// - ``localizedUppercased``
22112211
/// - ``lowercased``
2212+
/// - ``min(_:_:_:)``
22122213
/// - ``min(_:)``
22132214
/// - ``min(_:filter:)``
2215+
/// - ``max(_:_:_:)``
22142216
/// - ``max(_:)``
22152217
/// - ``max(_:filter:)``
22162218
/// - ``sum(_:)``

GRDB/QueryInterface/SQL/SQLFunctions.swift

+47-15
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public func abs(_ value: some SQLSpecificExpressible) -> SQLExpression {
1111
}
1212

1313
#if GRDBCUSTOMSQLITE || GRDBCIPHER
14-
/// The `AVG` SQL function.
14+
/// The `AVG` SQL aggregate function.
1515
///
1616
/// For example:
1717
///
@@ -26,7 +26,7 @@ public func average(
2626
.aggregateFunction("AVG", [value.sqlExpression], filter: filter?.sqlExpression)
2727
}
2828
#else
29-
/// The `AVG` SQL function.
29+
/// The `AVG` SQL aggregate function.
3030
///
3131
/// For example:
3232
///
@@ -44,7 +44,7 @@ public func average(
4444
filter: filter.sqlExpression)
4545
}
4646

47-
/// The `AVG` SQL function.
47+
/// The `AVG` SQL aggregate function.
4848
///
4949
/// For example:
5050
///
@@ -147,8 +147,24 @@ public func length(_ value: some SQLSpecificExpressible) -> SQLExpression {
147147
.function("LENGTH", [value.sqlExpression])
148148
}
149149

150+
/// The `MAX` SQL multi-argument function.
151+
///
152+
/// For example:
153+
///
154+
/// ```swift
155+
/// // MAX(score, 1000)
156+
/// max(Column("score"), 1000)
157+
/// ```
158+
public func max(
159+
_ value1: any SQLSpecificExpressible,
160+
_ value2: any SQLExpressible,
161+
_ values: any SQLExpressible...
162+
) -> SQLExpression {
163+
.simpleFunction("MAX", [value1.sqlExpression, value2.sqlExpression] + values.map(\.sqlExpression))
164+
}
165+
150166
#if GRDBCUSTOMSQLITE || GRDBCIPHER
151-
/// The `MAX` SQL function.
167+
/// The `MAX` SQL aggregate function.
152168
///
153169
/// For example:
154170
///
@@ -163,7 +179,7 @@ public func max(
163179
.aggregateFunction("MAX", [value.sqlExpression], filter: filter?.sqlExpression)
164180
}
165181
#else
166-
/// The `MAX` SQL function.
182+
/// The `MAX` SQL aggregate function.
167183
///
168184
/// For example:
169185
///
@@ -179,7 +195,7 @@ public func max(
179195
.aggregateFunction("MAX", [value.sqlExpression], filter: filter.sqlExpression)
180196
}
181197

182-
/// The `MAX` SQL function.
198+
/// The `MAX` SQL aggregate function.
183199
///
184200
/// For example:
185201
///
@@ -192,8 +208,24 @@ public func max(_ value: some SQLSpecificExpressible) -> SQLExpression {
192208
}
193209
#endif
194210

211+
/// The `MIN` SQL multi-argument function.
212+
///
213+
/// For example:
214+
///
215+
/// ```swift
216+
/// // MIN(score, 1000)
217+
/// min(Column("score"), 1000)
218+
/// ```
219+
public func min(
220+
_ value1: any SQLSpecificExpressible,
221+
_ value2: any SQLExpressible,
222+
_ values: any SQLExpressible...
223+
) -> SQLExpression {
224+
.simpleFunction("MIN", [value1.sqlExpression, value2.sqlExpression] + values.map(\.sqlExpression))
225+
}
226+
195227
#if GRDBCUSTOMSQLITE || GRDBCIPHER
196-
/// The `MIN` SQL function.
228+
/// The `MIN` SQL aggregate function.
197229
///
198230
/// For example:
199231
///
@@ -208,7 +240,7 @@ public func min(
208240
.aggregateFunction("MIN", [value.sqlExpression], filter: filter?.sqlExpression)
209241
}
210242
#else
211-
/// The `MIN` SQL function.
243+
/// The `MIN` SQL aggregate function.
212244
///
213245
/// For example:
214246
///
@@ -224,7 +256,7 @@ public func min(
224256
.aggregateFunction("MIN", [value.sqlExpression], filter: filter.sqlExpression)
225257
}
226258

227-
/// The `MIN` SQL function.
259+
/// The `MIN` SQL aggregate function.
228260
///
229261
/// For example:
230262
///
@@ -238,7 +270,7 @@ public func min(_ value: some SQLSpecificExpressible) -> SQLExpression {
238270
#endif
239271

240272
#if GRDBCUSTOMSQLITE || GRDBCIPHER
241-
/// The `SUM` SQL function.
273+
/// The `SUM` SQL aggregate function.
242274
///
243275
/// For example:
244276
///
@@ -262,7 +294,7 @@ public func sum(
262294
filter: filter?.sqlExpression)
263295
}
264296
#else
265-
/// The `SUM` SQL function.
297+
/// The `SUM` SQL aggregate function.
266298
///
267299
/// For example:
268300
///
@@ -284,7 +316,7 @@ public func sum(
284316
filter: filter.sqlExpression)
285317
}
286318

287-
/// The `SUM` SQL function.
319+
/// The `SUM` SQL aggregate function.
288320
///
289321
/// For example:
290322
///
@@ -302,7 +334,7 @@ public func sum(_ value: some SQLSpecificExpressible) -> SQLExpression {
302334
#endif
303335

304336
#if GRDBCUSTOMSQLITE || GRDBCIPHER
305-
/// The `TOTAL` SQL function.
337+
/// The `TOTAL` SQL aggregate function.
306338
///
307339
/// For example:
308340
///
@@ -326,7 +358,7 @@ public func total(
326358
filter: filter?.sqlExpression)
327359
}
328360
#else
329-
/// The `TOTAL` SQL function.
361+
/// The `TOTAL` SQL aggregate function.
330362
///
331363
/// For example:
332364
///
@@ -348,7 +380,7 @@ public func total(
348380
filter: filter.sqlExpression)
349381
}
350382

351-
/// The `TOTAL` SQL function.
383+
/// The `TOTAL` SQL aggregate function.
352384
///
353385
/// For example:
354386
///

Tests/GRDBTests/QueryInterfaceExpressionsTests.swift

+26-4
Original file line numberDiff line numberDiff line change
@@ -1559,7 +1559,18 @@ class QueryInterfaceExpressionsTests: GRDBTestCase {
15591559
"SELECT LENGTH(\"name\") FROM \"readers\"")
15601560
}
15611561

1562-
func testMinExpression() throws {
1562+
func testMultiArgumentMinExpression() throws {
1563+
let dbQueue = try makeDatabaseQueue()
1564+
1565+
XCTAssertEqual(
1566+
sql(dbQueue, tableRequest.select(min(Col.age, 1000))),
1567+
"SELECT MIN(\"age\", 1000) FROM \"readers\"")
1568+
XCTAssertEqual(
1569+
sql(dbQueue, tableRequest.select(min(Col.age, 1000, Col.id))),
1570+
"SELECT MIN(\"age\", 1000, \"id\") FROM \"readers\"")
1571+
}
1572+
1573+
func testAggregateMinExpression() throws {
15631574
let dbQueue = try makeDatabaseQueue()
15641575

15651576
XCTAssertEqual(
@@ -1570,7 +1581,7 @@ class QueryInterfaceExpressionsTests: GRDBTestCase {
15701581
"SELECT MIN(\"age\" / 2) FROM \"readers\"")
15711582
}
15721583

1573-
func testMinExpression_filter() throws {
1584+
func testAggregateMinExpression_filter() throws {
15741585
#if GRDBCUSTOMSQLITE || GRDBCIPHER
15751586
// Prevent SQLCipher failures
15761587
guard Database.sqliteLibVersionNumber >= 3030000 else {
@@ -1592,7 +1603,18 @@ class QueryInterfaceExpressionsTests: GRDBTestCase {
15921603
"SELECT MIN(\"age\" / 2) FILTER (WHERE \"age\" > 0) FROM \"readers\"")
15931604
}
15941605

1595-
func testMaxExpression() throws {
1606+
func testMultiArgumentMaxExpression() throws {
1607+
let dbQueue = try makeDatabaseQueue()
1608+
1609+
XCTAssertEqual(
1610+
sql(dbQueue, tableRequest.select(max(Col.age, 1000))),
1611+
"SELECT MAX(\"age\", 1000) FROM \"readers\"")
1612+
XCTAssertEqual(
1613+
sql(dbQueue, tableRequest.select(max(Col.age, 1000, Col.id))),
1614+
"SELECT MAX(\"age\", 1000, \"id\") FROM \"readers\"")
1615+
}
1616+
1617+
func testAggregateMaxExpression() throws {
15961618
let dbQueue = try makeDatabaseQueue()
15971619

15981620
XCTAssertEqual(
@@ -1603,7 +1625,7 @@ class QueryInterfaceExpressionsTests: GRDBTestCase {
16031625
"SELECT MAX(\"age\" / 2) FROM \"readers\"")
16041626
}
16051627

1606-
func testMaxExpression_filter() throws {
1628+
func testAggregateMaxExpression_filter() throws {
16071629
#if GRDBCUSTOMSQLITE || GRDBCIPHER
16081630
// Prevent SQLCipher failures
16091631
guard Database.sqliteLibVersionNumber >= 3030000 else {

0 commit comments

Comments
 (0)