3.5 KiB
MySQL Basics
This guide assumes you've set up MySQL and are connected to MySQL using a connection pool as described in the setup guide.
Type safety
The MySQL driver is written to embrace type-safety and Codable. We currently only expose Codable based results until we've found a good design for the non-codable API.
Queries
Queries are any type conforming to the protocol Query, which requires being convertible to a String.
String is a Query by default.
You can receive results from Queries in 3 kinds of formats.
- Stream
- Future
- forEach
All examples assume the following model:
struct User {
var username: String
var passwordHash: String
var age: Int
}
Streams
Streams, as described on this page, are a source of information that calls a single reader's callback. Streams are best used in larger datasets to prevent the query from consuming a large amount of memory. The downside of a stream is that you cannot return all results in a single future. You'll need to stream the results to the other endpoint, too. For HTTP this is described here.
Querying a database for a stream of results is achieved through the stream function and requires specifying the Decodable type that the results need to be deserialized into.
// `ModelStream<User>`
let usersStream = connectionPool.stream(User.self, in: "SELECT * FROM users")
This stream will return all results in the ModelStream's output callback which you can drain. You can register a callback on usersStream.onClose that will trigger when the end of the ModelStream has been reached.
Futures
Futures are often easier to use but significantly heavier on your memory and thus performance. They are thoroughly described here
Querying a database for a future is achieved through the all function and requires specifying the Decodable type that the results need to be deserialized into.
// Future<[User]>
let users = connectionPool.all(User.self, in: "SELECT * FROM users")
For partial results (SELECT username, age FROM) it is recommended to create a second decodable struct specifically for this query to ensure correctness and type-safety.
struct UserLoginDetails: Decodable {
var username: String
var age: Int
}
ForEach
If you don't need to stream complex results to a third party such as using an HTTP Response you can use forEach. This is particularly useful for asynchronous actions such as sending a lot of email to the results of a query without depending on the completion/success of one email for the next email.
connectionPool.forEach(User.self, in: "SELECT * FROM users") { user in
print(user.username)
}
forEach returns a future that you can optionally capture. It will be completed when all users have been processed.
let completed = connectionPool.forEach(User.self, in: "SELECT * FROM users") { user in
print(user.username)
}
completed.then {
print("All users printed")
}
Resultless queries
Some queries (mostly administrative queries) do not require/return a response. Instead, they only indicate success or error.
You can execute these queries using the query command.
connectionPool.query("DROP TABLE users")
You can handle success or response using the returned future.
connectionPool.query("DROP TABLE users").then {
print("success")
}.catch {
print("failure")
}