SQLite Overview¶
Let's dive into the vapor/sqlite package and see how to connect to and query a database.
Warning
This documentation provides an overview for the SQLite API. If you are using SQLite with Fluent, you will likely never need to use this API. Use Fluent's APIs instead.
Follow the instructions in the package section to add the SQLite package to your project. Once its added, you should be able to use import SQLite.
Database¶
The first step to making a query is to create a Database.
In Memory¶
In-memory SQLite databases are great for testing as they aren't persisted between application boots.
import SQLite let db = Database(storage: .memory)
File path¶
SQLite requires a single file to persist the database contents.
import SQLite let db = Database(storage: .file(path: "/tmp/db.sqlite"))
Tip
If the database file does not already exist, it will be created.
Connection¶
Once you have initialized your database, you can create a connection.
let conn = try db.makeConnection(on: .global())
Note
Pay special attention to which DispatchQueue you pass to makeConnection(on:).
This will be the queue SQLite calls you back on.
Tip
If you are using SQLite with Vapor, make sure to pass the worker's queue here.
Query¶
Once you have a Connection, you can use it to create a Query.
let query = conn.query("SELECT * FROM users")
Binding Values¶
If you are executing a query that has input values, you should bind these using parameters.
let query = conn.query("INSERT INTO users (name, age) VALUES (?, ?)") query.bind("Albert") query.bind(138)
You can also bind values using method chaining.
let query = conn.query("INSERT INTO users (name, age) VALUES (?, ?)") .bind("Albert") .bind(138)
Output Stream¶
If you expect output from your query, you must attach a stream. The easiest way
to do this is by using the drain convenience.
query.drain { row in print(row) }
You can also use drain(into:) and pass in a custom InputStream to capture the query's results.
Row¶
The Query will output Rows. These are simple structs.
struct Row { var fields: [String: Field] subscript(field: String) -> Data? { get } }
You can subscript a Row object to get the optional Data.
let nameData = row["name"] // Data?
Data is an enum that contains all possible types of SQLite data.
public enum Data { case integer(Int) case float(Double) case text(String) case blob(Foundation.Data) case null }
For each option, there are convenience properties for casting the Data enum.
let name = row["name"]?.text // String
Run¶
Once your query is ready to execute, you simply call .execute(). This returns a Future<Void>
that will be completed when the query is done executing.
query.execute().then { print("done!") }
All¶
If you simply want to fetch all of the results, you can use the .all() convenience.
This will automatically create a stream and return a future containing your results.
query.all().then { rows in print(rows) }
Sync¶
For situations where blocking is appropriate (perhaps in tests) you can use .sync() to block
until the query's results are ready and return them directly.
// don't do this unless blocking is OK let rows = try conn.query("SELECT * FROM users").sync()
Example¶
Now for the complete example:
import SQLite let db = Database(storage: .memory) let conn = try db.makeConnection(on: .global()) // take care to use correct queue conn.query("SELECT * FROM users") .all() .then { rows in print(rows) } .catch { err in print(err) } // wait for results
An example with values being bound:
import SQLite let db = Database(storage: .memory) let conn = try db.makeConnection(on: .global()) // take care to use correct queue conn.query("INSERT INTO users (name, age) VALUES (?, ?)") .bind("Albert") .bind(138) .execute() .then { print("done") } .catch { err in print(err) } // wait for results