From 3528a0fba0f8b08997e8b04ebf966f3b92d61d89 Mon Sep 17 00:00:00 2001 From: Tanner Nelson Date: Mon, 2 Oct 2017 19:12:28 -0400 Subject: [PATCH 1/2] sqlite 3.0 --- 3.0/docs/async/{index.md => package.md} | 0 3.0/docs/images/droplet-color.svg | 30 ++++ 3.0/docs/images/droplet-white.svg | 18 +++ 3.0/docs/sqlite/overview.md | 197 ++++++++++++++++++++++++ 3.0/docs/sqlite/package.md | 43 ++++++ 3.0/docs/stylesheets/extra.css | 48 ++++++ 3.0/mkdocs.yml | 5 +- 7 files changed, 340 insertions(+), 1 deletion(-) rename 3.0/docs/async/{index.md => package.md} (100%) create mode 100644 3.0/docs/images/droplet-color.svg create mode 100644 3.0/docs/images/droplet-white.svg create mode 100644 3.0/docs/sqlite/overview.md create mode 100644 3.0/docs/sqlite/package.md create mode 100644 3.0/docs/stylesheets/extra.css diff --git a/3.0/docs/async/index.md b/3.0/docs/async/package.md similarity index 100% rename from 3.0/docs/async/index.md rename to 3.0/docs/async/package.md diff --git a/3.0/docs/images/droplet-color.svg b/3.0/docs/images/droplet-color.svg new file mode 100644 index 00000000..11c540fb --- /dev/null +++ b/3.0/docs/images/droplet-color.svg @@ -0,0 +1,30 @@ + + + Vapor + The Vapor droplet logo in pink and blue. + + + + + + + + + + \ No newline at end of file diff --git a/3.0/docs/images/droplet-white.svg b/3.0/docs/images/droplet-white.svg new file mode 100644 index 00000000..9f645196 --- /dev/null +++ b/3.0/docs/images/droplet-white.svg @@ -0,0 +1,18 @@ + + + Vapor + The Vapor droplet logo in white. + + + + \ No newline at end of file diff --git a/3.0/docs/sqlite/overview.md b/3.0/docs/sqlite/overview.md new file mode 100644 index 00000000..e63b1008 --- /dev/null +++ b/3.0/docs/sqlite/overview.md @@ -0,0 +1,197 @@ +# SQLite Overview + +Let's dive into the [vapor/sqlite](https://github.com/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](../fluent/overview.md) instead. + +Follow the instructions in the [package](package.md) 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. + +```swift +import SQLite + +let db = Database(storage: .memory) +``` + +### File path + +SQLite requires a single file to persist the database contents. + +```swift +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. + +```swift +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](async/worker.md)'s queue here. + +## Query + +Once you have a `Connection`, you can use it to create a `Query`. + +```swift +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. + +```swift +let query = conn.query("INSERT INTO users (name, age) VALUES (?, ?)") +query.bind("Albert") +query.bind(138) +``` + +You can also bind values using method chaining. + +```swift +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. + +```swift +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 `Row`s. These are simple structs. + +```swift +struct Row { + var fields: [String: Field] + subscript(field: String) -> Data? { get } +} + +``` + +You can subscript a `Row` object to get the optional `Data`. + +```swift +let nameData = row["name"] // Data? +``` + +`Data` is an enum that contains all possible types of SQLite data. + +```swift +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. + +```swift +let name = row["name"]?.text // String +``` + + +### Run + +Once your query is ready to execute, you simply call `.execute()`. This returns a `Future` +that will be completed when the query is done executing. + +```swift +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. + +#### 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. + +```swift +// don't do this unless blocking is OK +let rows = try conn.query("SELECT * FROM users").sync() +``` + +### Example + +Now for the complete example: + +```swift +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: + +```swift +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 +``` diff --git a/3.0/docs/sqlite/package.md b/3.0/docs/sqlite/package.md new file mode 100644 index 00000000..261c8356 --- /dev/null +++ b/3.0/docs/sqlite/package.md @@ -0,0 +1,43 @@ +# Using SQLite + +The [vapor/sqlite](https://github.com/vapor/sqlite) package is a lightweight, nonblocking/async wrapper around SQLite 3's C API. It provides an intuitive Swift interface for working with SQLite that can be used with any Swift project. + +On top of `[vapor/sqlite](https://github.com/vapor/sqlite)`, we have built `[vapor/fluent-sqlite](https://github.com/vapor/fluent-sqlite)`. This package conforms our SQLite wrapper to Fluent's [driver](fluent/driver.md) protocol, allowing SQLite databases to be used with Fluent. + +## With Fluent + +This package is included with Fluent by default, and serves as Fluent's default database type. To use SQLite with Fluent, just [include Fluent])(fluent/package.md) in your project. + +Then use the following to import `SQLite`. + +```swift +import SQLite +``` + +If you need access to the Fluent driver specifically, use: + +```swift +import FluentSQLite +``` + +## Without Fluent + +This package was built to be a powerful interface for SQLite whether or not you use Fluent. To include this SQLite package in your project, simply add it to your Package manifest. + +```swift +// swift-tools-version:4.0 +import PackageDescription + +let package = Package( + name: "Project", + dependencies: [ + ... + .package(url: "https://github.com/vapor/sqlite.git", .upToNextMajor(from: "3.0.0")), + ], + targets: [ + .target(name: "Project", dependencies: ["Async", ... ]) + ] +) +``` + +Use `import SQLite` to access the Swift SQLite APIs. diff --git a/3.0/docs/stylesheets/extra.css b/3.0/docs/stylesheets/extra.css new file mode 100644 index 00000000..27fff6f2 --- /dev/null +++ b/3.0/docs/stylesheets/extra.css @@ -0,0 +1,48 @@ +.md-header { + box-shadow: 0px 0px 8px #657590!important; +} + +.md-main__inner { + padding-top: 0; +} + +.md-nav__button img { + width: 90%; +} + +.md-logo { + padding: 6px; +} + +.md-sidebar__inner .md-logo { + width: 60px!important; +} + +.md-header-nav__title { + line-height: 4.6rem; +} + +@media only screen and (min-width:75em) { + .md-header-nav__button img { + width: 28px!important; + height: 28px!important; + } + + .md-main__inner { + padding-top: 1rem; + } +} + +@media only screen and (min-width:100em) { + .md-header-nav__button img { + width: 32px!important; + height: 32px!important; + } +} + +@media only screen and (min-width:125em) { + .md-header-nav__button img { + width: 38px!important; + height: 38px!important; + } +} diff --git a/3.0/mkdocs.yml b/3.0/mkdocs.yml index f98abb76..51f065c0 100644 --- a/3.0/mkdocs.yml +++ b/3.0/mkdocs.yml @@ -10,7 +10,7 @@ pages: - 'Route Collection': 'vapor/route-collection.md' - 'Route Group': 'vapor/route-group.md' - Async: - - 'Index': 'async/index.md' + - 'Package': 'async/package.md' - 'Promise & Future': 'async/promise-future.md' - 'Stream': 'async/stream.md' - HTTP: @@ -45,6 +45,9 @@ pages: - 'Message authentication': 'crypto/mac.md' - 'Password hashing': 'crypto/passwords.md' - 'Random': 'crypto/random.md' +- SQLite: + - 'Package': 'sqlite/package.md' + - 'Overview': 'sqlite/overview.md' # - 'Advanced': # - 'RequestParser': 'http/request-parser.md' # - 'RequestSerializer': 'http/request-serializer.md' From 81a8100140c04bfac5f21e73358cac72fa8700a3 Mon Sep 17 00:00:00 2001 From: Tanner Nelson Date: Mon, 2 Oct 2017 19:13:13 -0400 Subject: [PATCH 2/2] .all() example --- 3.0/docs/sqlite/overview.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/3.0/docs/sqlite/overview.md b/3.0/docs/sqlite/overview.md index e63b1008..75d040bb 100644 --- a/3.0/docs/sqlite/overview.md +++ b/3.0/docs/sqlite/overview.md @@ -144,6 +144,12 @@ query.execute().then { 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. +```swift +query.all().then { rows in + print(rows) +} +``` + #### Sync For situations where blocking is appropriate (perhaps in tests) you can use `.sync()` to block