Skip to content

Commit

Permalink
3.0.0 GM (#103)
Browse files Browse the repository at this point in the history
* fix maintainers link

* fix maintainers link

* docblock / file cleanup

* implement SQLJoinBuilder, fixes #77

* Add KeyDecodingStrategy to SQLRow Decode (#82)

* Add serialize support for LIKE and NOT LIKE Binary Operators

* added JoinBuilder to SelectBuilder

* fix documentation

* removed old changed

* removed change from other PR

* fixed documentation

* fixed table name in tests

* remove test join comment

* readd like and not like

* removed SQLJoinBinaryExpression and SQLTableIdentifier. Updated indentation.

* removed SQLJoinBinaryExpression and SQLTableIdentifier, updated indentation.

* made join default to inner

* added custom key decoding strategy to SQLRowDecoder

* removed code from separate PR

* Removed Code from separate PR

* remove more code for separate PR

* removed more code for separate pr

* removed code for a separate pr

* all new updates

* add test

* added array decoding

* added minimum iOS version

* Added options to SQLRowDecoder. Made KeyDecodingStrategy an enum inside SQLRowDecoder.

* remove left over debuting code

* added a custom keyDecodingStrategy test

* changed keyPrefix back to prefix, added comments

* removed a _convertFromSnakeCase implementation in favour of the implementation to Foundation's JSONEncoder.swift

* make SQLRowDecoder init explicit

Co-authored-by: {rnantes} <{[email protected]}>

* added key encoding strategy (#101)

Co-authored-by: {rnantes} <{[email protected]}>

* updates

* where(...) overloads

* select section

* select + insert

* add .insert()

* update

* where group

* update

* delete

* raw

* updates

* remove dead code

Co-authored-by: Reid Nantes <[email protected]>
Co-authored-by: {rnantes} <{[email protected]}>
  • Loading branch information
3 people authored Apr 24, 2020
1 parent f777df2 commit 1e02396
Show file tree
Hide file tree
Showing 32 changed files with 1,047 additions and 450 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md → .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

- [@gwynne](https://github.com/gwynne)

See the [Vapor maintainers doc](https://github.com/vapor/vapor/blob/master/Docs/maintainers.md) for more information.
See the [Vapor maintainers doc](https://github.com/vapor/vapor/blob/master/.github/maintainers.md) for more information.
291 changes: 271 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,271 @@
<p align="center">
<img src="https://user-images.githubusercontent.com/1342803/58835528-3523e400-8624-11e9-8128-4925c7c9cf08.png" height="64" alt="SQLKit">
<br>
<br>
<a href="https://docs.vapor.codes/4.0/">
<img src="http://img.shields.io/badge/read_the-docs-2196f3.svg" alt="Documentation">
</a>
<a href="https://discord.gg/vapor">
<img src="https://img.shields.io/discord/431917998102675485.svg" alt="Team Chat">
</a>
<a href="LICENSE">
<img src="http://img.shields.io/badge/license-MIT-brightgreen.svg" alt="MIT License">
</a>
<a href="https://github.com/vapor/sql-kit/actions">
<img src="https://github.com/vapor/sql-kit/workflows/test/badge.svg" alt="Continuous Integration">
</a>
<a href="https://swift.org">
<img src="http://img.shields.io/badge/swift-5.2-brightgreen.svg" alt="Swift 5.2">
</a>
</p>
<img src="https://user-images.githubusercontent.com/1342803/58835528-3523e400-8624-11e9-8128-4925c7c9cf08.png" height="64" alt="SQLKit">
<br>
<a href="https://docs.vapor.codes/4.0/">
<img src="http://img.shields.io/badge/read_the-docs-2196f3.svg" alt="Documentation">
</a>
<a href="https://discord.gg/vapor">
<img src="https://img.shields.io/discord/431917998102675485.svg" alt="Team Chat">
</a>
<a href="LICENSE">
<img src="http://img.shields.io/badge/license-MIT-brightgreen.svg" alt="MIT License">
</a>
<a href="https://github.com/vapor/sql-kit/actions">
<img src="https://github.com/vapor/sql-kit/workflows/test/badge.svg" alt="Continuous Integration">
</a>
<a href="https://swift.org">
<img src="http://img.shields.io/badge/swift-5.2-brightgreen.svg" alt="Swift 5.2">
</a>
<br>
<br>

Build SQL queries in Swift. Extensible, protocol-based design that supports DQL, DML, and DDL.

### Major Releases

The table below shows a list of PostgresNIO major releases alongside their compatible NIO and Swift versions.

|Version|NIO|Swift|SPM|
|-|-|-|-|
|3.0|2.0+|5.2+|`from: "3.0.0"`|
|2.0|1.0+|4.0+|`from: "2.0.0"`|
|1.0|n/a|4.0+|`from: "1.0.0"`|

Use the SPM string to easily include the dependendency in your `Package.swift` file.

```swift
.package(url: "https://github.com/vapor/sql-kit.git", from: ...)
```

### Supported Platforms

PostgresNIO supports the following platforms:

- Ubuntu 16.04+
- macOS 10.15+

## Overview

SQLKit is an API for building and serializing SQL queries in Swift. SQLKit attempts to abstract away SQL dialect inconsistencies where possible allowing you to write queries that can run on multiple database flavors. Where abstraction is not possible, SQLKit provides powerful APIs for custom or dynamic behavior.

### Supported Databases

These database packages are built on SQLKit:

- [vapor/postgres-kit](https://github.com/vapor/postgres-kit): PostgreSQL
- [vapor/mysql-kit](https://github.com/vapor/mysql-kit): MySQL and MariaDB
- [vapor/sqlite-kit](https://github.com/vapor/sqlite-kit): SQLite

### Configuration

SQLKit does not deal with creating or managing database connections itself. This package is focused entirely around building and serializing SQL queries. To connect to your SQL database, refer to your specific database package's documentation. Once you are connected to your database and have an instance of `SQLDatabase`, you are ready to continue.

### Database

Instances of `SQLDatabase` are capable of serializing and executing `SQLExpression`.

```swift
let db: SQLDatabase = ...
db.execute(sql: SQLExpression, onRow: (SQLRow) -> ())
```

`SQLExpression` is a protocol that represents a SQL query string and optional bind values. It can represent an entire SQL query or just a fragment.

SQLKit provides `SQLExpression`s for common queries like `SELECT`, `UPDATE`, `INSERT`, `DELETE`, `CREATE TABLE`, and more.

```swift
var select = SQLSelect()
select.columns = [...]
select.tables = [...]
select.predicate = ...
```

`SQLDatabase` can be used to create fluent query builders for most of these query types.

```swift
let planets = try db.select()
.column("*")
.from("planets")
.where("name", .equal, "Earth")
.all().wait()
```

You can execute a query builder by calling `run()`.

### Rows

For query builders that support returning results, like `select()`, there are additional methods for handling the database output.

- `all()`: Returns an array of rows.
- `first()`: Returns an optional row.
- `run(_:)`: Accepts a closure that handles rows as they are returned.

Each of these methods returns `SQLRow` which has methods for access column values.

```swift
let row: SQLRow
let name = try row.decode(column: "name", as: String.self)
print(name) // String
```

### Codable

`SQLRow` also supports decoding `Codable` models directly from a row.

```swift
struct Planet: Codable {
var name: String
}

let planet = try row.decode(model: Planet.self)
```

Query builders that support returning results have convenience methods for automatically decoding models.

```swift
let planets = try db.select()
...
.all(decoding: Planet.self).wait()
```

## Select

The `select()` method creates a `SELECT` query builder.

```swift
let planets = try db.select()
.columns("id", "name")
.from("planets")
.where("name", .equal, "Earth")
.all().wait()
```

This code would generate the following SQL:

```sql
SELECT id, name FROM planets WHERE name = ?
```

Notice that `Encodable` values are automatically bound as parameters instead of being serialized directly to the query.

The select builder has the following methods.

- `columns`
- `from`
- `where` (`orWhere`)
- `limit`
- `offset`
- `groupBy`
- `having` (`orHaving`)
- `distinct`
- `for` (`lockingClause`)
- `join`

By default, query components like `where` will be joined by `AND`. Methods prefixed with `or` exist for joining by `OR`.

```swift
builder.where("name", .equal, "Earth").orWhere("name", .equal, "Mars")
```

This code would generate the following SQL:

```sql
name = ? OR name = ?
```

`where` also supports creating grouped clauses.

```swift
builder.where("name", .notEqual, SQLLiteral.null).where {
$0.where("name", .equal, SQLBind("Milky Way"))
.orWhere("name", .equal, SQLBind("Andromeda"))
}
```

This code generates the following SQL:

```sql
name != NULL AND (name == ? OR name == ?)
```

## Insert

The `insert(into:)` method creates an `INSERT` query builder.

```swift
try db.insert(into: "galaxies")
.columns("id", "name")
.values(SQLLiteral.default, SQLBind("Milky Way"))
.values(SQLLiteral.default, SQLBind("Andromeda"))
.run().wait()
```

This code would generate the following SQL:

```sql
INSERT INTO galaxies (id, name) VALUES (DEFAULT, ?) (DEFAULT, ?)
```

The insert builder also has a method for encoding a `Codable` type as a set of values.

```swift
struct Galaxy: Codable {
var name: String
}

try builder.model(Galaxy(name: "Milky Way"))
```

## Update

The `update(_:)` method creates an `UPDATE` query builder.

```swift
try db.update("planets")
.where("name", .equal, "Jpuiter")
.set("name", to: "Jupiter")
.run().wait()
```

This code generates the following SQL:

```sql
UPDATE planets SET name = ? WHERE name = ?
```

The update builder supports the same `where` and `orWhere` methods as the select builder.

## Delete

The `delete(from:)` method creates a `DELETE` query builder.

```swift
try db.delete(from: "planets")
.where("name", .equal, "Jupiter")
.run().wait()
```

This code generates the following SQL:

```sql
DELETE FROM planets WHERE name = ?
```

The delete builder supports the same `where` and `orWhere` methods as the select builder.

## Raw

The `raw(_:)` method allows for passing custom SQL query strings with support for parameterized binds.

```swift
let table = "planets"
let planets = try db.raw("SELECT * FROM \(table) WHERE name = \(bind: planet)")
.all().wait()
```

This code generates the following SQL:

```sql
SELECT * FROM planets WHERE name = ?
```

The `\(bind:)` interpolation should be used for any user input to avoid SQL injection.
11 changes: 2 additions & 9 deletions Sources/SQLKit/Builders/SQLAlterTableBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,18 @@ public final class SQLAlterTableBuilder: SQLQueryBuilder {
/// `SQLAlterTable` query being built.
public var alterTable: SQLAlterTable

/// See `SQLQueryBuilder`.
public var database: SQLDatabase

/// See `SQLQueryBuilder`.
public var query: SQLExpression {
return self.alterTable
}

/// See `SQLColumnBuilder`.

public var columns: [SQLExpression] {
get { return alterTable.addColumns }
set { alterTable.addColumns = newValue }
}

/// Creates a new `SQLAlterTableBuilder`.
///
/// - parameters:
/// - alterTable: Alter table query.
/// - connection: Connection to perform query on.
public init(_ alterTable: SQLAlterTable, on database: SQLDatabase) {
self.alterTable = alterTable
self.database = database
Expand Down Expand Up @@ -172,7 +165,7 @@ public final class SQLAlterTableBuilder: SQLQueryBuilder {
extension SQLDatabase {
/// Creates a new `SQLAlterTableBuilder`.
///
/// conn.alter(table: "planets")...
/// db.alter(table: "planets")...
///
/// - parameters:
/// - table: Table to alter.
Expand Down
6 changes: 3 additions & 3 deletions Sources/SQLKit/Builders/SQLCreateEnumBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
extension SQLDatabase {
/// Creates a new `SQLCreateEnumBuilder`.
///
/// conn.create(enum: "meal", cases: "breakfast", "lunch", "dinner")...
/// db.create(enum: "meal", cases: "breakfast", "lunch", "dinner")...
///
/// - parameters:
/// - name: Name of ENUM type to create.
Expand All @@ -15,7 +15,7 @@ extension SQLDatabase {

/// Creates a new `SQLCreateEnumBuilder`.
///
/// conn.create(enum: SQLIdentifier("meal"), cases: "breakfast", "lunch", "dinner")...
/// db.create(enum: SQLIdentifier("meal"), cases: "breakfast", "lunch", "dinner")...
///
/// - parameters:
/// - name: Name of ENUM type to create.
Expand All @@ -28,7 +28,7 @@ extension SQLDatabase {

/// Builds `SQLCreateEnum` queries.
///
/// conn.create(enum: "meal", cases: "breakfast", "lunch", "dinner")
/// db.create(enum: "meal", cases: "breakfast", "lunch", "dinner")
/// .run()
///
/// See `SQLColumnBuilder` and `SQLQueryBuilder` for more information.
Expand Down
Loading

0 comments on commit 1e02396

Please sign in to comment.