Skip to content

Commit 0189d4f

Browse files
authored
Merge branch 'main' into tagged-trait
2 parents d5fd836 + 13ce4ee commit 0189d4f

31 files changed

+458
-671
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ struct Reminder {
3131
var title = ""
3232
var isCompleted = false
3333
var priority: Int?
34-
@Column(as: Date.ISO8601Representation?.self)
3534
var dueDate: Date?
3635
}
3736
```

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

Lines changed: 111 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@ you can use the static description of its properties to build type-safe queries.
1414
schema of your app is defined first and foremost in your database, and then you define Swift types
1515
that represent those database definitions.
1616

17+
* [Defining a table](#Defining-a-table)
18+
* [Customizing a table](#Customizing-a-table)
19+
* [Table names](#Table-names)
20+
* [Column names](#Column-names)
21+
* [Custom data types](#Custom-data-types)
22+
* [RawRepresentable](#RawRepresentable)
23+
* [JSON](#JSON)
24+
* [Default representations for dates and UUIDs](#Default-representations-for-dates-and-UUIDs)
25+
* [Primary keyed tables](#Primary-keyed-tables)
26+
* [Ephemeral columns](#Ephemeral-columns)
27+
1728
### Defining a table
1829

1930
Suppose your database has a table defined with the following create statement:
@@ -152,113 +163,6 @@ with your table's columns, instead. For these data types you must either define
152163
The library comes with several `QueryRepresentable` conformances to aid in representing dates,
153164
UUIDs, and JSON, and you can define your own conformances for your own custom data types.
154165
155-
#### Dates
156-
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:
159-
160-
* Text column interpreted as ISO-8601-formatted string.
161-
* Int column interpreted as number of seconds since Unix epoch.
162-
* Double column interpreted as a Julian day (number of days since November 24, 4713 BC).
163-
164-
Because of this ambiguity, the `@Table` macro does not know what you intend when you define a data
165-
type like this:
166-
167-
```swift
168-
@Table struct Reminder {
169-
let id: Int
170-
var date: Date // 🛑 'Date' column requires a query representation
171-
}
172-
```
173-
174-
In order to make it explicit how you expect to turn a `Date` into a type that SQLite understands
175-
(_i.e._ text, integer, or double) you can use the `@Column` macro with the `as:` argument. The
176-
library comes with 3 strategies out the box to help, ``Foundation/Date/ISO8601Representation`` for
177-
storing the date as a string, ``Foundation/Date/UnixTimeRepresentation`` for storing the date as an
178-
integer, and ``Foundation/Date/JulianDayRepresentation`` for storing the date as a floating point
179-
number.
180-
181-
Any of these representations can be used like so:
182-
183-
```swift
184-
@Table struct Reminder {
185-
let id: Int
186-
@Column(as: Date.ISO8601Representation.self)
187-
var date: Date
188-
}
189-
```
190-
191-
And StructuredQueries will take care of formatting the value for the database:
192-
193-
@Row {
194-
@Column {
195-
```swift
196-
Reminder.insert(
197-
Reminder.Draft(date: Date())
198-
)
199-
```
200-
}
201-
@Column {
202-
```sql
203-
INSERT INTO "reminders"
204-
("date")
205-
VALUES
206-
('2018-01-29 00:08:00.000')
207-
```
208-
}
209-
}
210-
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-
219-
#### UUID
220-
221-
SQLite also does not have native support for UUIDs. If you try to use a UUID in your tables you
222-
will get an error:
223-
224-
```swift
225-
@Table struct Reminder {
226-
let id: UUID // 🛑 'UUID' column requires a query representation
227-
var title = ""
228-
}
229-
```
230-
231-
To use such identifiers in your table you can store the column as a data blob, and then you can
232-
use the ``Foundation/UUID/BytesRepresentation`` column representation:
233-
234-
```swift
235-
@Table struct Reminder {
236-
@Column(as: UUID.BytesRepresentation.self)
237-
let id: UUID
238-
var title = ""
239-
}
240-
```
241-
242-
Alternatively you can store the column as text and use either
243-
``Foundation/UUID/LowercasedRepresentation`` or ``Foundation/UUID/UppercasedRepresentation`` to
244-
translate the UUID to text:
245-
246-
```swift
247-
@Table struct Reminder {
248-
@Column(as: UUID.LowercasedRepresentation.self)
249-
let id: UUID
250-
var title = ""
251-
}
252-
```
253-
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-
262166
#### RawRepresentable
263167
264168
Simple data types, in particular ones conforming to `RawRepresentable` whose `RawValue` is a string
@@ -398,6 +302,106 @@ RemindersList.leftJoin(Reminder.all) {
398302
$0.id == $1.id // 🛑 Requires the types 'Reminder.ID' and 'RemindersList.ID' be equivalent
399303
}
400304
305+
#### Default representations for dates and UUIDs
306+
307+
While some relational databases, like MySQL and Postgres, have native types for dates and UUIDs,
308+
SQLite does _not_, and instead can represent them in a variety of ways. In order to lessen the
309+
friction of building queries with dates and UUIDs, the library has decided to provide a default
310+
representation for dates and UUIDs, and if that choice does not fit your schema you can explicitly
311+
specify the representation you want.
312+
313+
##### Dates
314+
315+
Dates in SQLite have 3 different representations:
316+
317+
* Text column interpreted as ISO-8601-formatted string.
318+
* Int column interpreted as number of seconds since Unix epoch.
319+
* Double column interpreted as a Julian day (number of days since November 24, 4713 BC).
320+
321+
By default, StructuredQueries will bind and decode dates as ISO-8601 text. If you want the library
322+
to use a different representation (_i.e._ integer or double), you can provide an explicit query
323+
representation to the `@Column` macro's `as:` argument. ``Foundation/Date/UnixTimeRepresentation``
324+
will store the date as an integer, and ``Foundation/Date/JulianDayRepresentation`` will store the
325+
date as a floating point number.
326+
327+
For example:
328+
329+
```swift
330+
@Table struct Reminder {
331+
let id: Int
332+
@Column(as: Date.UnixTimeRepresentation.self)
333+
var date: Date
334+
}
335+
```
336+
337+
And StructuredQueries will take care of formatting the value for the database:
338+
339+
@Row {
340+
@Column {
341+
```swift
342+
Reminder.insert(
343+
Reminder.Draft(date: Date())
344+
)
345+
```
346+
}
347+
@Column {
348+
```sql
349+
INSERT INTO "reminders"
350+
("date")
351+
VALUES
352+
(1517184480)
353+
```
354+
}
355+
}
356+
357+
If you use the non-default date representation in your schema, then while querying against a
358+
date column with a Swift Date, you will need to explicitly bundle up the Swift date into the
359+
appropriate representation to use various query helpers. This can be done using the `#bind` macro:
360+
361+
```swift
362+
Reminder.where { $0.created > #bind(startDate) }
363+
```
364+
365+
> Note: When using the default representation for dates (ISO-8601 text) you do not need to use
366+
> the `#bind` macro:
367+
>
368+
> ```swift
369+
> Reminder.where { $0.created > startDate }
370+
> ```
371+
372+
##### UUIDs
373+
374+
SQLite also does not have type-level support for UUIDs. By default, the library will bind and decode
375+
UUIDs as lowercased, hexadecimal text, but it also provides custom representations. This includes
376+
``Foundation/UUID/UppercasedRepresentation`` for uppercased text, as well as
377+
``Foundation/UUID/BytesRepresentation`` for raw bytes.
378+
379+
To use such custom representations, you can provide it to the `@Column` macro's `as:` parameter:
380+
381+
```swift
382+
@Table struct Reminder {
383+
@Column(as: UUID.BytesRepresentation.self)
384+
let id: UUID
385+
var title = ""
386+
}
387+
```
388+
389+
If you use the non-default UUID representation in your schema, then while querying against a UUID
390+
column with a Swift UUID, you will need to explicitly bundle up the Swift UUID into the appropriate
391+
representation to use various query helpers. This can be done using
392+
the `#bind` macro:
393+
394+
```swift
395+
Reminder.where { $0.id != #bind(reminder.id) }
396+
```
397+
398+
> Note: When using the default representation for UUID (lower-cased text) you do not need to use
399+
> the `#bind` macro:
400+
>
401+
> ```swift
402+
> Reminder.where { $0.id != reminder.id }
403+
> ```
404+
401405
### Primary keyed tables
402406
403407
It is possible to tell let the `@Table` macro know which property of your data type is the primary

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,14 @@ could be restored for a certain amount of time. These tables can be represented
2626
struct RemindersList: Identifiable {
2727
let id: Int
2828
var title = ""
29-
@Column(as: Date.ISO8601Representation?.self)
3029
var deletedAt: Date?
3130
}
3231
@Table
3332
struct Reminder: Identifiable {
3433
let id: Int
3534
var title = ""
3635
var isCompleted = false
37-
@Column(as: Date.ISO8601Representation?.self)
3836
var dueAt: Date?
39-
@Column(as: Date.ISO8601Representation?.self)
4037
var deletedAt: Date?
4138
var remindersListID: RemindersList.ID
4239
}
@@ -207,7 +204,6 @@ struct Reminder {
207204
let id: Int
208205
var title = ""
209206
var isCompleted = false
210-
@Column(as: Date.ISO8601Representation?.self)
211207
var deletedAt: Date?
212208

213209
static let all = Self.where { $0.isDeleted.isNot(nil) }

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@
1010

1111
### Conformances
1212

13-
- ``Foundation/Date/ISO8601Representation``
13+
- ``Swift/Decodable/JSONRepresentation``
1414
- ``Foundation/Date/JulianDayRepresentation``
1515
- ``Foundation/Date/UnixTimeRepresentation``
1616
- ``Foundation/UUID/BytesRepresentation``
17-
- ``Foundation/UUID/LowercasedRepresentation``
1817
- ``Foundation/UUID/UppercasedRepresentation``
19-
- ``Swift/Decodable/JSONRepresentation``
18+
19+
### Deprecations
20+
21+
- ``Foundation/Date/ISO8601Representation``
22+
- ``Foundation/UUID/LowercasedRepresentation``

Sources/StructuredQueriesCore/Documentation.docc/StructuredQueriesCore.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ struct Reminder {
1515
var title = ""
1616
var isCompleted = false
1717
var priority: Int?
18-
@Column(as: Date.ISO8601Representation?.self)
1918
var dueDate: Date?
2019
}
2120
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@_exported import StructuredQueriesSupport

0 commit comments

Comments
 (0)