Skip to content

Commit a22b47b

Browse files
committed
Merge remote-tracking branch 'origin/main' into coding-keys
2 parents 5de1b89 + 009190f commit a22b47b

22 files changed

Lines changed: 568 additions & 230 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
name: macOS
1919
strategy:
2020
matrix:
21-
xcode: ["26.2"]
21+
xcode: ["26.6"]
2222
runs-on: macos-26
2323
steps:
2424
- uses: actions/checkout@v5

Sources/StructuredQueries/Macros.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -210,12 +210,6 @@ public macro Column(
210210
type: "ColumnMacro"
211211
)
212212

213-
/// Customizes a group of columns generated by the ``/StructuredQueriesCore/Table`` protocol.
214-
///
215-
/// - Parameters:
216-
/// - primaryKey: These columns are the table's composite primary key.
217-
/// - lazyInitializable: Optionalize this column group in the generated `Draft` type so it can be
218-
/// initialized later (by you, or by the database on insert).
219213
@available(*, deprecated, renamed: "Column")
220214
@attached(peer)
221215
public macro Columns(

Sources/StructuredQueriesCore/Documentation.docc/Articles/GettingStarted.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,8 @@ Notice that you can return any number of orders for the query as a tuple, and yo
254254
which orders are in a descending versus ascending fashion.
255255

256256
And finally, suppose we wanted to further customize the above query by limiting the results to 10
257-
rows and selecting the 2nd page of results. This can be done using the ``Table/limit(_:offset:)``
258-
method:
257+
rows and selecting the 2nd page of results. This can be done using the ``Table/limit(_:)``
258+
and ``Table/offset(_:)`` methods:
259259

260260
@Row {
261261
@Column {
@@ -272,7 +272,8 @@ method:
272272
$0.priority.desc(),
273273
$0.title)
274274
}
275-
.limit(10, offset: 10)
275+
.limit(10)
276+
.offset(10)
276277
// => [String]
277278
```
278279
}

Sources/StructuredQueriesCore/Documentation.docc/Articles/ScalarFunctions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Explore the full list of available functions below.
4040

4141
- ``QueryExpression/??(_:_:)``
4242
- ``QueryExpression/ifnull(_:)``
43+
- ``QueryExpression/nullif(_:)``
4344

4445
### Bytes
4546

Sources/StructuredQueriesCore/Documentation.docc/Articles/SelectStatements.md

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -691,7 +691,8 @@ enum Ordering {
691691

692692
### Paginating results
693693

694-
The `limit(_:offset:)` function is used to change a query's `LIMIT` and `OFFSET` clauses.
694+
The `limit(_:)` and `offset(_:)` functions are used to change a query's `LIMIT` and `OFFSET`
695+
clauses.
695696

696697
@Row {
697698
@Column {
@@ -710,7 +711,9 @@ The `limit(_:offset:)` function is used to change a query's `LIMIT` and `OFFSET`
710711
@Row {
711712
@Column {
712713
```swift
713-
Reminder.limit(10, offset: 10)
714+
Reminder
715+
.limit(10)
716+
.offset(10)
714717
```
715718
}
716719
@Column {
@@ -721,53 +724,62 @@ The `limit(_:offset:)` function is used to change a query's `LIMIT` and `OFFSET`
721724
}
722725
}
723726

724-
Multiple chained calls to `limit` will override the limit and offset to the last call, using the
725-
existing offset if none is provided:
727+
Multiple chained calls will override the clause of the last call:
726728

727729
@Row {
728730
@Column {
729731
```swift
730732
Reminder
731-
.limit(10, offset: 10)
733+
.limit(10)
734+
.offset(10)
732735
.limit(20)
733736
```
734737
}
735738
@Column {
736739
```sql
737740
SELECT … FROM "reminders"
738-
LIMIT 20
741+
LIMIT 20 OFFSET 10
739742
```
740743
}
741744
}
742745

746+
Passing `nil` will leave the clause untouched, which makes it easy to apply a limit or offset from
747+
optional data:
748+
743749
@Row {
744750
@Column {
745751
```swift
752+
let offset: Int? = nil
746753
Reminder
747754
.limit(10)
748-
.limit(20, offset: 20)
755+
.offset(offset)
749756
```
750757
}
751758
@Column {
752759
```sql
753760
SELECT … FROM "reminders"
754-
LIMIT 20 OFFSET 20
761+
LIMIT 10
755762
```
756763
}
757764
}
758765

766+
And both functions have result builder variants that can build the clause from the query's tables,
767+
where producing no expression is equivalent to passing `nil`:
768+
759769
@Row {
760770
@Column {
761771
```swift
762-
Reminder
763-
.limit(10, offset: 10)
764-
.limit(20, offset: 20)
772+
Reminder.limit { _ in
773+
if isPaginated {
774+
pageSize
775+
}
776+
}
765777
```
766778
}
767779
@Column {
768780
```sql
769781
SELECT … FROM "reminders"
770-
LIMIT 20 OFFSET 20
782+
LIMIT 10
771783
```
772784
}
773785
}

Sources/StructuredQueriesCore/Documentation.docc/Extensions/Select.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
- ``group(by:)``
1717
- ``having(_:)``
1818
- ``order(by:)``
19-
- ``limit(_:offset:)``
19+
- ``limit(_:)``
20+
- ``offset(_:)``
2021
- ``count(filter:)``
2122
- ``find(_:)``
2223

Sources/StructuredQueriesCore/Documentation.docc/Extensions/Table.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
- ``group(by:)``
1616
- ``having(_:)``
1717
- ``order(by:)``
18-
- ``limit(_:offset:)``
18+
- ``limit(_:)``
19+
- ``offset(_:)``
1920
- ``count(filter:)``
2021
- ``insert(_:values:onConflict:where:doUpdate:where:)``
2122
- ``insert(_:select:onConflict:where:doUpdate:where:)``

Sources/StructuredQueriesCore/Internal/Deprecations.swift

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,109 @@
11
import Foundation
22

3+
// NB: Deprecated after 0.33.3:
4+
5+
extension Table {
6+
@available(*, deprecated, message: "Use 'limit(_:)' and 'offset(_:)', instead.")
7+
public static func limit(
8+
_ maxLength: (TableColumns) -> some QueryExpression<Int>,
9+
offset: ((TableColumns) -> some QueryExpression<Int>)?
10+
) -> SelectOf<Self> {
11+
Where().limit(maxLength, offset: offset)
12+
}
13+
14+
@available(*, deprecated, message: "Use 'limit(_:)' and 'offset(_:)', instead.")
15+
public static func limit(_ maxLength: Int, offset: Int?) -> SelectOf<Self> {
16+
Where().limit(maxLength, offset: offset)
17+
}
18+
}
19+
20+
extension Where {
21+
@available(*, deprecated, message: "Use 'limit(_:)' and 'offset(_:)', instead.")
22+
public func limit(
23+
_ maxLength: (From.TableColumns) -> some QueryExpression<Int>,
24+
offset: ((From.TableColumns) -> some QueryExpression<Int>)?
25+
) -> SelectOf<From> {
26+
asSelect().limit(maxLength, offset: offset)
27+
}
28+
29+
@available(*, deprecated, message: "Use 'limit(_:)' and 'offset(_:)', instead.")
30+
public func limit(_ maxLength: Int, offset: Int?) -> SelectOf<From> {
31+
asSelect().limit(maxLength, offset: offset)
32+
}
33+
}
34+
35+
extension Select {
36+
@_disfavoredOverload
37+
@available(*, deprecated, message: "Use 'limit(_:)' and 'offset(_:)', instead.")
38+
public func limit<each J: Table>(
39+
_ maxLength: (From.TableColumns, repeat (each J).TableColumns) -> some QueryExpression<Int>,
40+
offset: ((From.TableColumns, repeat (each J).TableColumns) -> any QueryExpression<Int>)?
41+
) -> Self
42+
where Joins == (repeat each J) {
43+
limit(maxLength(From.columns, repeat (each J).columns))
44+
.offset(offset?(From.columns, repeat (each J).columns))
45+
}
46+
47+
@_disfavoredOverload
48+
@available(*, deprecated, message: "Use 'limit(_:)' and 'offset(_:)', instead.")
49+
public func limit(
50+
_ maxLength: (From.TableColumns, Joins.TableColumns) -> some QueryExpression<Int>,
51+
offset: ((From.TableColumns, Joins.TableColumns) -> any QueryExpression<Int>)?
52+
) -> Self
53+
where Joins: Table {
54+
limit(maxLength(From.columns, Joins.columns))
55+
.offset(offset?(From.columns, Joins.columns))
56+
}
57+
58+
@available(*, deprecated, message: "Use 'limit(_:)' and 'offset(_:)', instead.")
59+
public func limit<each J: Table>(_ maxLength: Int, offset: Int?) -> Self
60+
where Joins == (repeat each J) {
61+
limit(maxLength).offset(offset)
62+
}
63+
}
64+
65+
// NB: Deprecated after 0.33.1:
66+
67+
extension QueryExpression where QueryValue == String {
68+
@available(*, deprecated, message: "Prefer 'like(\"\\(other)%\")' instead")
69+
public func hasPrefix(_ other: some StringProtocol) -> some QueryExpression<Bool> {
70+
like("\(other)%")
71+
}
72+
73+
@available(*, deprecated, message: "Prefer 'like(\"%\\(other)\")' instead")
74+
public func hasSuffix(_ other: some StringProtocol) -> some QueryExpression<Bool> {
75+
like("%\(other)")
76+
}
77+
78+
@_disfavoredOverload
79+
@available(*, deprecated, message: "Prefer 'like(\"%\\(other)%\")' instead")
80+
public func contains(_ other: some StringProtocol) -> some QueryExpression<Bool> {
81+
return like("%\(other)%")
82+
}
83+
}
84+
85+
extension Sequence where Element: QueryBindable {
86+
@available(*, deprecated, message: "Prefer 'element.in(self)' instead")
87+
public func contains(
88+
_ element: some QueryExpression<Element.QueryValue>
89+
) -> some QueryExpression<Bool> {
90+
element.in(self)
91+
}
92+
}
93+
94+
extension ClosedRange where Bound: QueryBindable {
95+
@available(
96+
*,
97+
deprecated,
98+
message: "Prefer 'element.between(lowerBound, and: upperBound)' instead"
99+
)
100+
public func contains(
101+
_ element: some QueryExpression<Bound.QueryValue>
102+
) -> some QueryExpression<Bool> {
103+
element.between(lowerBound, and: upperBound)
104+
}
105+
}
106+
3107
// NB: Deprecated after 0.32.0:
4108

5109
extension TableDraft {

Sources/StructuredQueriesCore/Operators.swift

Lines changed: 0 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -707,52 +707,6 @@ extension QueryExpression where QueryValue == String {
707707
) -> some QueryExpression<Bool> {
708708
LikeOperator(string: self, pattern: "\(pattern)", escape: escape)
709709
}
710-
711-
/// A predicate expression from this string expression matched against another _via_ the `LIKE`
712-
/// operator given a prefix.
713-
///
714-
/// ```swift
715-
/// Reminder.where { $0.title.hasPrefix("get") }
716-
/// // SELECT … FROM "reminders" WHERE ("reminders"."title" LIKE 'get%')
717-
/// ```
718-
///
719-
/// - Parameter other: A string expression describing the prefix.
720-
/// - Returns: A predicate expression.
721-
@available(*, deprecated, message: "Prefer 'like(\"\\(other)%\")' instead")
722-
public func hasPrefix(_ other: some StringProtocol) -> some QueryExpression<Bool> {
723-
like("\(other)%")
724-
}
725-
726-
/// A predicate expression from this string expression matched against another _via_ the `LIKE`
727-
/// operator given a suffix.
728-
///
729-
/// ```swift
730-
/// Reminder.where { $0.title.hasSuffix("get") }
731-
/// // SELECT … FROM "reminders" WHERE ("reminders"."title" LIKE '%get')
732-
/// ```
733-
///
734-
/// - Parameter other: A string expression describing the suffix.
735-
/// - Returns: A predicate expression.
736-
@available(*, deprecated, message: "Prefer 'like(\"%\\(other)\")' instead")
737-
public func hasSuffix(_ other: some StringProtocol) -> some QueryExpression<Bool> {
738-
like("%\(other)")
739-
}
740-
741-
/// A predicate expression from this string expression matched against another _via_ the `LIKE`
742-
/// operator given an infix.
743-
///
744-
/// ```swift
745-
/// Reminder.where { $0.title.contains("get") }
746-
/// // SELECT … FROM "reminders" WHERE ("reminders"."title" LIKE '%get%')
747-
/// ```
748-
///
749-
/// - Parameter other: A string expression describing the infix.
750-
/// - Returns: A predicate expression.
751-
@_disfavoredOverload
752-
@available(*, deprecated, message: "Prefer 'like(\"%\\(other)%\")' instead")
753-
public func contains(_ other: some StringProtocol) -> some QueryExpression<Bool> {
754-
return like("%\(other)%")
755-
}
756710
}
757711

758712
extension SQLQueryExpression<String> {
@@ -855,42 +809,6 @@ extension QueryExpression where QueryValue: QueryExpression {
855809
}
856810
}
857811

858-
extension Sequence where Element: QueryBindable {
859-
/// Returns a predicate expression indicating whether the sequence contains the given expression.
860-
///
861-
/// An alias for ``QueryExpression/in(_:)``, flipped.
862-
///
863-
/// - Parameter element: An element.
864-
/// - Returns: A predicate expression indicating whether the expression is in this sequence
865-
@available(*, deprecated, message: "Prefer 'element.in(self)' instead")
866-
public func contains(
867-
_ element: some QueryExpression<Element.QueryValue>
868-
) -> some QueryExpression<Bool> {
869-
element.in(self)
870-
}
871-
}
872-
873-
extension ClosedRange where Bound: QueryBindable {
874-
/// Returns a predicate expression indicating whether the given expression is contained within
875-
/// this range.
876-
///
877-
/// An alias for ``QueryExpression/between(_:and:)``, flipped.
878-
///
879-
/// - Parameter element: An element.
880-
/// - Returns: A predicate expression indicating whether the given element is between this range's
881-
/// bounds.
882-
@available(
883-
*,
884-
deprecated,
885-
message: "Prefer 'element.between(lowerBound, and: upperBound)' instead"
886-
)
887-
public func contains(
888-
_ element: some QueryExpression<Bound.QueryValue>
889-
) -> some QueryExpression<Bool> {
890-
element.between(lowerBound, and: upperBound)
891-
}
892-
}
893-
894812
extension Statement where QueryValue: QueryBindable {
895813
/// Returns a predicate expression indicating whether this subquery contains the given element.
896814
///

0 commit comments

Comments
 (0)