You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+15-15Lines changed: 15 additions & 15 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -13,9 +13,9 @@
13
13
</p>
14
14
15
15
# Overview
16
-
Otter is a pure Swift SQL compiler that allow developers to simply write plain SQL with compile time safety.
16
+
Otter is a pure Swift SQL compiler that allows developers to simply write plain SQL with compile time safety.
17
17
If your database schema changes, you will get compile time errors for the places that need to be fixed.
18
-
It just doesn't generate the code to talk to SQLite, but rather your entire data layer in a testable
18
+
It doesn't just generate the code to talk to SQLite, but rather your entire data layer in a testable
19
19
flexible manner. No more writing mocks or wrappers. Just pass in the query.
20
20
21
21
-[Installation](#installation)
@@ -79,7 +79,7 @@ Otter can even run within a Swift macro by adding the `@Database` macro to a `st
79
79
@Database
80
80
structDB {
81
81
@Query("SELECT * FROM foo")
82
-
varselectFooQuery: any SelectFooQuery
82
+
varselectFoo: any SelectFooQuery
83
83
84
84
staticvar migrations: [String] {
85
85
return [
@@ -106,7 +106,7 @@ func main() async throws {
106
106
107
107
> [!IMPORTANT]
108
108
> As of now it is not recommended for larger projects. There are quite a few limitations
109
-
that won't scale well beyond a fairly simple schema and a handfull of queries.
109
+
that won't scale well beyond a fairly simple schema and a handful of queries.
110
110
111
111
#### Anatomy of a @Query
112
112
```swift
@@ -205,7 +205,7 @@ let database = try DB(path: "...")
205
205
let database =try DB.inMemory()
206
206
207
207
// Or open up using the configuration.
208
-
var config =DatbaseConfig()
208
+
var config =DatabaseConfig()
209
209
config.path=""// if nil, it will be in memory
210
210
config.maxConnectionCount=8
211
211
let database =tryDB(config: config)
@@ -267,15 +267,15 @@ SELECT id, name FROM user;
267
267
In the example above, since we selected all columns from a single table the query will return the `User` struct that was generated for the table. If additional columns are selected a new structure will be generated to match the selected columns. In the following example we will join in the `post` table to get a users post count.
268
268
```sql
269
269
fetchUsers:
270
-
SELECT user.*, COUNT(post.*) AS numberOfPosts
271
-
OUTER JOIN post ONpost.userId=user.id
270
+
SELECT user.*, COUNT(*) AS numberOfPosts
271
+
LEFT OUTER JOIN post ONpost.userId=user.id
272
272
GROUP BYuser.id;
273
273
```
274
274
275
275
The following `struct` would automatically be generated for the query. Since we used the syntax `user.*` it will embed the `User` struct instead of replicating it's columns. Any embeded table struct will also get a `@dynamicMemberLookup` method generated so it can be accessed directly like the other column values. This allows extensions on the table struct to work across many queries.
276
276
```swift
277
277
@dynamicMemberLookup
278
-
FetchUsersOutput {
278
+
structFetchUsersOutput {
279
279
let user: User
280
280
let numberOfPosts: Int
281
281
@@ -284,7 +284,7 @@ FetchUsersOutput {
284
284
```
285
285
286
286
### Inputs
287
-
When a query has multiple inputs it will have a struct generated for it's inputs similar to the output. Also, so the input struct does not have to be initialized everytime, an extension will be created that takes each parameter individually, rather then the full type.
287
+
When a query has multiple inputs it will have a struct generated for it's inputs similar to the output. Also, so the input struct does not have to be initialized every time, an extension will be created that takes each parameter individually, rather then the full type.
288
288
```sql
289
289
userPosts:
290
290
SELECT*FROM post WHERE userId = ? ANDdate BETWEEN ? AND ?;
@@ -294,13 +294,13 @@ Would generate the following Swift code
294
294
295
295
```swift
296
296
structUserPostsInput {
297
-
letid: Int
297
+
letuserId: Int
298
298
let dateLower: Date
299
299
let dateUpper: Date
300
300
}
301
301
302
302
// Using the extension
303
-
let posts =tryawait database.userQueries.userPosts.execute(id: id, dateLower: lower, dateUpper: upper)
303
+
let posts =tryawait database.userQueries.userPosts.execute(userId: id, dateLower: lower, dateUpper: upper)
304
304
305
305
// Or using the input type directly
306
306
let posts =tryawait database.userQueries.userPosts.execute(with: UserPostInput(...))
SQLite is a unique SQL database engine in that it is fairly lawless when it comes to typing. SQLite will allow you create a column with an `INTEGER` and gladly insert a `TEXT` into it. It will even let you make up your own type names and it will take them. Otter will not allow this and tends to operate more strictly like the table option `STRICT`. Only the core types that SQLite recognizes are usable for the column type.
317
+
SQLite is a unique SQL database engine in that it is fairly lawless when it comes to typing. SQLite will allow you to create a column with an `INTEGER` and gladly insert a `TEXT` into it. It will even let you make up your own type names and it will take them. Otter will not allow this and tends to operate more strictly like the table option `STRICT`. Only the core types that SQLite recognizes are usable for the column type.
318
318
| SQLite | Swift |
319
319
|---------|--------|
320
320
| INTEGER | Int |
@@ -339,7 +339,7 @@ TEXT AS "Todo.ID"
339
339
## Dependency Injection
340
340
> TL;DR Avoid the repository pattern, inject queries.
341
341
342
-
Otter was written with application development in mind. One of the common walls when talking to a database is dependecy injection.
342
+
Otter was written with application development in mind. One of the pain points when talking to a database is dependency injection.
343
343
Normally this would mean wrapping your database calls in a repository or some other layer to keep the model layer testable without needing a database connection.
344
344
This is all good but that means writing different protocols and mocks. When writing the protocol you need to decide whether to just make it `async` or maybe a `publisher`.
345
345
Sometimes you need both... Otter solves these problems and was designed to have injection builtin.
0 commit comments