Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ This module also contains all of the macros that support the core functionality

See [`StructuredQueriesCore`](<doc:/StructuredQueriesCore>) for general library usage.

StructuredQueries also ships SQLite-specific helpers:

- [`StructuredQueriesSQLiteCore`](<doc:/StructuredQueriesSQLiteCore>): Core, SQLite-specific
functionality, including full-text search, type-safe temporary triggers, full-text search, and
more.

- [`StructuredQueriesSQLite`](<doc:/StructuredQueriesSQLite>): Everything from
`StructuredQueriesSQLiteCore` and macros that support it, like `@DatabaseFunction.`

## Topics

### Macros
Expand Down
2 changes: 1 addition & 1 deletion Sources/StructuredQueriesCore/AggregateFunctions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ public struct AggregateFunction<QueryValue>: QueryExpression, Sendable {
var order: QueryFragment?
var filter: QueryFragment?

init(
package init(
_ name: QueryFragment,
isDistinct: Bool = false,
_ arguments: [QueryFragment] = [],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Defining your schema

Learn how to replicate your database's schema in first class Swift types
using the `@Table` and `@Column` macros.
Learn how to replicate your database's schema in first class Swift types using the `@Table` and
`@Column` macros.

## Overview

Expand All @@ -22,7 +22,6 @@ that represent those database definitions.
* [RawRepresentable](#RawRepresentable)
* [JSON](#JSON)
* [Tagged identifiers](#Tagged-identifiers)
* [Default representations for dates and UUIDs](#Default-representations-for-dates-and-UUIDs)
* [Primary keyed tables](#Primary-keyed-tables)
* [Ephemeral columns](#Ephemeral-columns)
* [Generated columns](#Generated-columns)
Expand Down Expand Up @@ -306,106 +305,6 @@ RemindersList.leftJoin(Reminder.all) {
}
```

#### Default representations for dates and UUIDs

While some relational databases, like MySQL and Postgres, have native types for dates and UUIDs,
SQLite does _not_, and instead can represent them in a variety of ways. In order to lessen the
friction of building queries with dates and UUIDs, the library has decided to provide a default
representation for dates and UUIDs, and if that choice does not fit your schema you can explicitly
specify the representation you want.

##### Dates

Dates in SQLite have 3 different representations:

* Text column interpreted as ISO-8601-formatted string.
* Int column interpreted as number of seconds since Unix epoch.
* Double column interpreted as a Julian day (number of days since November 24, 4713 BC).

By default, StructuredQueries will bind and decode dates as ISO-8601 text. If you want the library
to use a different representation (_i.e._ integer or double), you can provide an explicit query
representation to the `@Column` macro's `as:` argument. ``Foundation/Date/UnixTimeRepresentation``
will store the date as an integer, and ``Foundation/Date/JulianDayRepresentation`` will store the
date as a floating point number.

For example:

```swift
@Table struct Reminder {
let id: Int
@Column(as: Date.UnixTimeRepresentation.self)
var date: Date
}
```

And StructuredQueries will take care of formatting the value for the database:

@Row {
@Column {
```swift
Reminder.insert {
Reminder.Draft(date: Date())
}
```
}
@Column {
```sql
INSERT INTO "reminders"
("date")
VALUES
(1517184480)
```
}
}

If you use the non-default date representation in your schema, then while querying against a
date column with a Swift Date, you will need to explicitly bundle up the Swift date into the
appropriate representation to use various query helpers. This can be done using the `#bind` macro:

```swift
Reminder.where { $0.created > #bind(startDate) }
```

> Note: When using the default representation for dates (ISO-8601 text) you do not need to use
> the `#bind` macro:
>
> ```swift
> Reminder.where { $0.created > startDate }
> ```

##### UUIDs

SQLite also does not have type-level support for UUIDs. By default, the library will bind and decode
UUIDs as lowercased, hexadecimal text, but it also provides custom representations. This includes
``Foundation/UUID/UppercasedRepresentation`` for uppercased text, as well as
``Foundation/UUID/BytesRepresentation`` for raw bytes.

To use such custom representations, you can provide it to the `@Column` macro's `as:` parameter:

```swift
@Table struct Reminder {
@Column(as: UUID.BytesRepresentation.self)
let id: UUID
var title = ""
}
```

If you use the non-default UUID representation in your schema, then while querying against a UUID
column with a Swift UUID, you will need to explicitly bundle up the Swift UUID into the appropriate
representation to use various query helpers. This can be done using
the `#bind` macro:

```swift
Reminder.where { $0.id != #bind(reminder.id) }
```

> Note: When using the default representation for UUID (lower-cased text) you do not need to use
> the `#bind` macro:
>
> ```swift
> Reminder.where { $0.id != reminder.id }
> ```

### Primary keyed tables

It is possible to let the `@Table` macro know which property of your data type is the primary
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ capable of. See <doc:SelectStatements> for more examples of select statements, a
The library provides the tools necessary to construct type-safe insert statements in SQL, including
inserting an entire value into a table, inserting only a subset of rows, as well as what to do on
conflicts. Using the `Reminder` data type from above, we can insert data for all of its rows using
the ``Table/insert(or:_:values:onConflict:where:doUpdate:where:)`` method:
the ``Table/insert(_:values:onConflict:where:doUpdate:where:)`` method:

@Row {
@Column {
Expand Down Expand Up @@ -339,7 +339,7 @@ and the number of columns and data type of each column must match what is specif
trailing closure.

You can provide a 3rd trailing closure to
``Table/insert(or:_:values:onConflict:where:doUpdate:where:)`` to describe what to do in case there
``Table/insert(_:values:onConflict:where:doUpdate:where:)`` to describe what to do in case there
is a conflict while inserting data. For example, suppose we had a unique index on the "title" column
of the reminders table. Then when inserting a value with a repeated title we could resolve the
conflict by appending the string `" (Copy)"` to the title:
Expand Down Expand Up @@ -451,7 +451,7 @@ queries.

The library provides tools for constructing type-safe update statements in SQL, including updating
all rows in a table, a filtered set of rows, or rows with specific primary keys.
``Table/update(or:set:)`` returns an update statement given a closure that describes the changes you
``Table/update(set:)`` returns an update statement given a closure that describes the changes you
want to make to the table by mutating an `inout` representation of the table's columns. You can
assign new values or even call a selection of mutating methods and in-place operators.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Learn how to build queries that insert data into a database.
### Inserting records

The simplest way to insert table records into the database is the
[`Table.insert`](<doc:Table/insert(or:_:values:onConflict:where:doUpdate:where:)>), which takes a
[`Table.insert`](<doc:Table/insert(_:values:onConflict:where:doUpdate:where:)>), which takes a
trailing closure and the record(s) to be inserted:

@Row {
Expand Down Expand Up @@ -191,7 +191,7 @@ As well as introduce conditional or looping logic:
### Inserting from a select statement

To insert a row into a table with the results of a ``Select`` statement, use
``Table/insert(or:_:select:onConflict:)``:
``Table/insert(_:select:onConflict:)``:

@Row {
@Column {
Expand Down Expand Up @@ -219,7 +219,7 @@ statement's columns.
### Inserting default values

To insert a row into a table where all values have database-provided defaults, use
``Table/insert(or:)``:
``Table/insert()``:

@Row {
@Column {
Expand Down Expand Up @@ -309,7 +309,7 @@ ReminderForm(
```

When the draft is ready to be committed back to the database, you can use
``PrimaryKeyedTable/upsert(or:values:)``, which generates an ``Insert`` with an "upsert" clause:
``PrimaryKeyedTable/upsert(values:)``, which generates an ``Insert`` with an "upsert" clause:

@Row {
@Column {
Expand All @@ -333,30 +333,7 @@ When the draft is ready to be committed back to the database, you can use

### Conflict resolution and upserts

Every insert function includes an optional `or` parameter, which can be used to specify the `OR`
clause for conflict resolution:

@Row {
@Column {
```swift
Tag.insert(or: .ignore) {
$0.title
} values: {
"home"
}
```
}
@Column {
```sql
INSERT OR IGNORE INTO "tags"
("title")
VALUES
('home')
```
}
}

And many include an optional upsert clause. You can unconditionally upsert using the
Most insert functions include an optional upsert clause. You can unconditionally upsert using the
`onConflictDoUpdate` trailing closure:

@Row {
Expand Down Expand Up @@ -446,24 +423,24 @@ Upsert clauses have an additional, special argument for referring to a row that
`WHERE` conditions are also supported, on both the conflict and update clauses.

> Tip: The `onConflictDoUpdate` and `doUpdate` closures work similarly to the closure parameter of
> ``Table/update(or:set:)``. See <doc:UpdateStatements> for more information on building these
> ``Table/update(set:)``. See <doc:UpdateStatements> for more information on building these
> clauses.

## Topics

### Inserting values

- ``Table/insert(or:_:values:onConflict:where:doUpdate:where:)``
- ``Table/insert(or:_:values:onConflictDoUpdate:where:)``
- ``Table/insert(or:)``
- ``Table/insert(_:values:onConflict:where:doUpdate:where:)``
- ``Table/insert(_:values:onConflictDoUpdate:where:)``
- ``Table/insert()``

### Inserting drafts

- ``PrimaryKeyedTable/upsert(or:values:)``
- ``PrimaryKeyedTable/upsert(values:)``

### Inserting from a select

- ``Table/insert(or:_:select:onConflict:where:doUpdate:where:)``
- ``Table/insert(_:select:onConflict:where:doUpdate:where:)``

### Upserts

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,3 @@ Explore the full list of operators below.
- ``PartialSelectStatement/exists()``
- ``Swift/Array``
- ``Swift/ClosedRange``

### Casting

- ``QueryExpression/cast(as:)``
- ``SQLiteType``
- ``SQLiteTypeAffinity``
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ let draft = Reminder.Draft(title: "Get groceries")

The `id` is not necessary to provide because it is optional. This allows you to insert rows into
your database without specifying the id. The library comes with a special
``PrimaryKeyedTable/insert(or:_:onConflict:)`` method that allows you to insert a row
into the database by providing only a draft:
``PrimaryKeyedTable/insert(_:onConflict:)`` method that allows you to insert a row into the database
by providing only a draft:

@Row {
@Column {
Expand Down Expand Up @@ -188,8 +188,8 @@ The ``PrimaryKeyedTable/find(_:)`` method can be used for updates and deletions
}


A special ``PrimaryKeyedTable/update(or:_:)`` method is also provided to update all the fields of a
row with the corresponding primary key:
A special ``PrimaryKeyedTable/update(_:)`` method is also provided to update all the fields of a row
with the corresponding primary key:

@Row {
@Column {
Expand Down
Loading