Skip to content

Commit 7b8e8f1

Browse files
committed
Merge remote-tracking branch 'origin/main' into default-date-uuid-representations
2 parents 88e578a + c773eb9 commit 7b8e8f1

File tree

10 files changed

+199
-229
lines changed

10 files changed

+199
-229
lines changed

Package.resolved

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,14 +143,19 @@ generated when writing queries will correctly use `"is_completed"`.
143143
144144
### Custom data types
145145
146-
There are many data types that do not have native support in SQLite. For these data types you must
147-
define a conformance to ``QueryBindable`` in order to translate values to a format that SQLite does
148-
understand. The library comes with conformances to aid in representing dates, UUIDs, and JSON, and
149-
you can define your own conformances for your own custom data types.
146+
StructuredQueries provides support for many basic Swift data types out of the box, like strings,
147+
integers, doubles, bytes, and booleans, but you may want to represent custom, domain specific types
148+
with your table's columns, instead. For these data types you must either define a conformance to
149+
``QueryBindable`` to translate values to a format that the library does understand, or provide a
150+
``QueryRepresentable`` type that wraps your domain type.
151+
152+
The library comes with several `QueryRepresentable` conformances to aid in representing dates,
153+
UUIDs, and JSON, and you can define your own conformances for your own custom data types.
150154
151155
#### Dates
152156
153-
SQLite does not have a native date type, and instead has 3 different ways to represent dates:
157+
While some relational databases, like MySQL and Postgres, have native support for dates, SQLite
158+
does _not_. Instead, it has 3 different ways to represent dates:
154159
155160
* Text column interpreted as ISO-8601-formatted string.
156161
* Int column interpreted as number of seconds since Unix epoch.
@@ -203,6 +208,14 @@ And StructuredQueries will take care of formatting the value for the database:
203208
}
204209
}
205210
211+
When querying against a date column with a Swift date, you will need to explicitly bundle up the
212+
Swift date into the appropriate representation to use various query helpers. This can be done using
213+
the `#bind` macro:
214+
215+
```swift
216+
Reminder.where { $0.created > #bind(startDate) }
217+
```
218+
206219
#### UUID
207220
208221
SQLite also does not have native support for UUIDs. If you try to use a UUID in your tables you
@@ -238,6 +251,14 @@ translate the UUID to text:
238251
}
239252
```
240253
254+
When querying against a UUID column with a Swift UUID, you will need to explicitly bundle up the
255+
Swift UUID into the appropriate representation to use various query helpers. This can be done using
256+
the `#bind` macro:
257+
258+
```swift
259+
Reminder.where { $0.id != #bind(reminder.id) }
260+
```
261+
241262
#### RawRepresentable
242263
243264
Simple data types, in particular ones conforming to `RawRepresentable` whose `RawValue` is a string

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,8 @@ The `limit(_:offset:)` function is used to change a query's `LIMIT` and `OFFSET`
721721
}
722722
}
723723

724-
Multiple chained calls to `limit` will override the limit and offset to the last call:
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:
725726

726727
@Row {
727728
@Column {
@@ -755,6 +756,22 @@ Multiple chained calls to `limit` will override the limit and offset to the last
755756
}
756757
}
757758

759+
@Row {
760+
@Column {
761+
```swift
762+
Reminder
763+
.limit(10, offset: 10)
764+
.limit(20, offset: 20)
765+
```
766+
}
767+
@Column {
768+
```sql
769+
SELECT … FROM "reminders"
770+
LIMIT 20 OFFSET 20
771+
```
772+
}
773+
}
774+
758775
### Compound selects
759776

760777
It is possible to combine multiple selects together using the `union`, `intersect`, and `except`

Sources/StructuredQueriesCore/Operators.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -861,7 +861,7 @@ extension QueryExpression where QueryValue: QueryBindable {
861861
BinaryOperator(
862862
lhs: self,
863863
operator: "BETWEEN",
864-
rhs: BinaryOperator<Void>(lhs: lowerBound, operator: "AND", rhs: upperBound)
864+
rhs: SQLQueryExpression("\(lowerBound) AND \(upperBound)")
865865
)
866866
}
867867
}

Sources/StructuredQueriesCore/Statements/Select.swift

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,8 +1282,8 @@ extension Select {
12821282
where Joins == (repeat each J) {
12831283
var select = self
12841284
select.limit = _LimitClause(
1285-
maxLength: maxLength(From.columns, repeat (each J).columns),
1286-
offset: offset?(From.columns, repeat (each J).columns)
1285+
maxLength: maxLength(From.columns, repeat (each J).columns).queryFragment,
1286+
offset: offset?(From.columns, repeat (each J).columns).queryFragment ?? select.limit?.offset
12871287
)
12881288
return select
12891289
}
@@ -1297,7 +1297,10 @@ extension Select {
12971297
public func limit<each J: Table>(_ maxLength: Int, offset: Int? = nil) -> Self
12981298
where Joins == (repeat each J) {
12991299
var select = self
1300-
select.limit = _LimitClause(maxLength: maxLength, offset: offset)
1300+
select.limit = _LimitClause(
1301+
maxLength: maxLength.queryFragment,
1302+
offset: offset?.queryFragment ?? select.limit?.offset
1303+
)
13011304
return select
13021305
}
13031306

@@ -1488,14 +1491,6 @@ public struct _LimitClause: QueryExpression {
14881491
let maxLength: QueryFragment
14891492
let offset: QueryFragment?
14901493

1491-
init(
1492-
maxLength: some QueryExpression,
1493-
offset: (some QueryExpression)? = Int?.none
1494-
) {
1495-
self.maxLength = maxLength.queryFragment
1496-
self.offset = offset?.queryFragment
1497-
}
1498-
14991494
public var queryFragment: QueryFragment {
15001495
var query: QueryFragment = "LIMIT \(maxLength)"
15011496
if let offset {

Sources/StructuredQueriesMacros/TableMacro.swift

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ extension TableMacro: ExtensionMacro {
3434
var diagnostics: [Diagnostic] = []
3535

3636
// NB: A compiler bug prevents us from applying the '@_Draft' macro directly
37-
var draftBindings: [(PatternBindingSyntax, queryOutputType: TypeSyntax?)] = []
37+
var draftBindings: [(PatternBindingSyntax, queryOutputType: TypeSyntax?, optionalize: Bool)] =
38+
[]
3839
// NB: End of workaround
3940

4041
var draftProperties: [DeclSyntax] = []
@@ -223,12 +224,8 @@ extension TableMacro: ExtensionMacro {
223224
)
224225
}
225226

226-
// NB: A compiled bug prevents us from applying the '@_Draft' macro directly
227-
if identifier == primaryKey?.identifier {
228-
draftBindings.append((binding.optionalized(), columnQueryOutputType))
229-
} else {
230-
draftBindings.append((binding, columnQueryOutputType))
231-
}
227+
// NB: A compiler bug prevents us from applying the '@_Draft' macro directly
228+
draftBindings.append((binding, columnQueryOutputType, identifier == primaryKey?.identifier))
232229
// NB: End of workaround
233230

234231
var assignedType: String? {
@@ -398,8 +395,12 @@ extension TableMacro: ExtensionMacro {
398395
.compactMap(\.memberBlock.members.trimmed)
399396
var memberwiseArguments: [PatternBindingSyntax] = []
400397
var memberwiseAssignments: [TokenSyntax] = []
401-
for (binding, queryOutputType) in draftBindings {
402-
let argument = binding.trimmed.annotated(queryOutputType).rewritten(selfRewriter)
398+
for (binding, queryOutputType, optionalize) in draftBindings {
399+
var argument = binding.trimmed
400+
if optionalize {
401+
argument = argument.optionalized()
402+
}
403+
argument = argument.annotated(queryOutputType).rewritten(selfRewriter)
403404
if argument.typeAnnotation == nil {
404405
let identifier =
405406
(argument.pattern.as(IdentifierPatternSyntax.self)?.identifier.trimmedDescription)

Tests/StructuredQueriesMacrosTests/SelectionMacroTests.swift

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -146,42 +146,5 @@ extension SnapshotTests {
146146
"""#
147147
}
148148
}
149-
150-
@Test func dateDiagnostic() {
151-
assertMacro {
152-
"""
153-
@Selection struct ReminderDate {
154-
var date: Date
155-
}
156-
"""
157-
} expansion: {
158-
#"""
159-
struct ReminderDate {
160-
var date: Date
161-
}
162-
163-
extension ReminderDate: StructuredQueriesCore.QueryRepresentable {
164-
public struct Columns: StructuredQueriesCore.QueryExpression {
165-
public typealias QueryValue = ReminderDate
166-
public let queryFragment: StructuredQueriesCore.QueryFragment
167-
public init(
168-
date: some StructuredQueriesCore.QueryExpression<Date>
169-
) {
170-
self.queryFragment = """
171-
\(date.queryFragment) AS "date"
172-
"""
173-
}
174-
}
175-
public init(decoder: inout some StructuredQueriesCore.QueryDecoder) throws {
176-
let date = try decoder.decode(Date.self)
177-
guard let date else {
178-
throw QueryDecodingError.missingRequiredColumn
179-
}
180-
self.date = date
181-
}
182-
}
183-
"""#
184-
}
185-
}
186149
}
187150
}

Tests/StructuredQueriesMacrosTests/Support/SnapshotTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@ import Testing
88
@Suite(
99
.serialized,
1010
.macros(
11-
record: .failed,
12-
macros: [
11+
[
1312
"_Draft": TableMacro.self,
1413
"bind": BindMacro.self,
1514
"Column": ColumnMacro.self,
1615
"Ephemeral": EphemeralMacro.self,
17-
"Table": TableMacro.self,
1816
"Selection": SelectionMacro.self,
1917
"sql": SQLMacro.self,
20-
]
18+
"Table": TableMacro.self,
19+
],
20+
record: .failed
2121
)
2222
) struct SnapshotTests {}
2323

0 commit comments

Comments
 (0)