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
brew install TODO FIX THIS ONCE IT IS ACTUALLY IN BREW
115
+
```
116
+
117
+
Once the project has been added it is time to setup the queries and migrations folders. In the root of the project where you want everything to live, in terminal run the following command
118
+
```
119
+
feather init
120
+
```
121
+
122
+
This will create all diretories needed and will create your first migration. Your project should have 2 new folders and a swift file. In the migrations folder you will have a file named `1.sql`. You put your first migration code in there. The `Queries.swift` file is what the generated Swift code will be written too. The `gen` command will automatically recreate this if it gets deleted.
123
+
```
124
+
/Migrations/1.sql
125
+
/Queries
126
+
Queries.swift
127
+
```
128
+
129
+
> 💡 Tip: Follow the SQL standard and use singular table names.
130
+
131
+
#### Generating the Database
132
+
Once you have your first migration in and the project setup you can now generate the database. In the same directory where `init` was run, you run the `gen` command.
133
+
```
134
+
feather gen
135
+
```
136
+
137
+
This will compile and check all migrations and queries, then generate all Swift required to talk to the database.
138
+
139
+
#### Adding a New Migration
140
+
When a new migration is needed, you can simply add a new file with a number 1 higher than the previous. To automatically do this the cli tool can do it for you by running
141
+
```
142
+
feather migrate add
143
+
```
144
+
145
+
# Opening a Connection
146
+
Once you have your database being generated, you can now open a connection to it. Each database will automatically have a few initializers at hand to choose from. Each are listed below. When the connection is opened, all migrations are run instantly.
98
147
99
148
All connections are automatically opened up in WAL journal mode, allowing asynchronous reads while writes are happening. And all connections will automatically handle all threading and scheduling of queries for you.
100
149
@@ -110,9 +159,75 @@ var config = DatbaseConfig()
110
159
config.path=""// if nil, it will be in memory
111
160
config.maxConnectionCount=8
112
161
let database =tryDB(config: config)
162
+
163
+
// All migrations are run on open, so it's good to use right away
164
+
```
165
+
166
+
# Queries
167
+
All queries will be stored in the `/Queries` directory. More than one query can go in each file. To get started, create a new file in the `/Queries` directory. The cli can do this automatically. In the same directory where `init` was run, execute
168
+
```
169
+
feather queries add --name <some-name>
170
+
```
171
+
172
+
> 💡 Tip: Organize queries by usage, not by table. This will become more useful later on when we talk about dependency injection.
173
+
174
+
Open the file that was created in `/Queries`, it should be blank. Individual queries can be defined using the `DEFINE` keyword. At the moment queries can only have one statement.
175
+
```sql
176
+
DEFINE QUERY fetchUsers AS
177
+
SELECT*FROM user;
178
+
```
179
+
180
+
If the queries file was named `User.sql` this would be accessible via
181
+
```swift
182
+
let query: any FetchUsersQuery = database.userQueries.fetchUsers
183
+
let users: [User] =tryawait query.execute()
184
+
```
185
+
186
+
### Input and Output Types
187
+
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 input. In the following example we will join in the `post` table to get a users post count.
188
+
```sql
189
+
DEFINE QUERY fetchUsers AS
190
+
SELECT user.*, COUNT(post.*) AS numberOfPosts
191
+
OUTER JOIN post ONpost.userId=user.id;
192
+
```
193
+
194
+
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.
The `FetchUsersOutput` name, while clear where it came from, is not too great if we want to store it in a view model or model within our app. Some queries we want to give it a better name that has more meaning. In the `DEFINE` statement we can specify a name for the inputs and outputs.
207
+
```sql
208
+
DEFINE QUERY queryName(input: InputName, output: OutputName) AS ...
209
+
```
210
+
211
+
### Inputs
212
+
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.
213
+
```sql
214
+
DEFINE QUERY userPosts AS
215
+
SELECT*FROM post WHERE userId = ? ANDdate BETWEEN ? AND ?;
216
+
```
217
+
218
+
Would generate the following Swift code
219
+
220
+
```swift
221
+
structUserPostsInput {
222
+
let id: Int
223
+
let dateLower: Date
224
+
let dateUpper: Date
225
+
}
226
+
227
+
let posts =tryawait database.userQueries.userPosts.execute(id: id, dateLower: lower, dateUpper: upper)
113
228
```
114
229
115
-
##Types
230
+
# Types
116
231
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 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.
0 commit comments