Skip to content

Commit

Permalink
2.0.0 gm (#182)
Browse files Browse the repository at this point in the history
* 2.0.0 gm

* file updates

* update guide docs

* fix img

* fix img

* fixes

* fixes

* fixes

* fixes

* fixes

* fixes

* fixes

* fixes

* code cleanup

* add search path

* add searchPath fixes #9

* optional password

* fix decoder force unwrap #175

* pass server hostname, fixes #178

* array type test

* test fluent gm branch

* master + import fix
  • Loading branch information
tanner0101 authored Apr 29, 2020
1 parent b465e9e commit 152f69b
Show file tree
Hide file tree
Showing 15 changed files with 269 additions and 126 deletions.
33 changes: 33 additions & 0 deletions .github/contributing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Contributing to PostgresKit

👋 Welcome to the Vapor team!

## Docker

This package includes a `docker-compose` file you can use for spinning up test databases with test credentials.

```sh
$ docker-compose up psql-11
```

## Testing

Once in Xcode, select the `postgres-kit` scheme and use `CMD+U` to run the tests.

You can also test via the CLI using `swift test`.

If you are fixing a single GitHub issue in particular, you can add a test named `testGH<issue number>` to ensure
that your fix is working. This will also help prevent regression.

## SemVer

Vapor follows [SemVer](https://semver.org). This means that any changes to the source code that can cause
existing code to stop compiling _must_ wait until the next major version to be included.

Code that is only additive and will not break any existing code can be included in the next minor release.

----------

Join us on Discord if you have any questions: [discord.gg/vapor](https://discord.gg/vapor).

&mdash; Thanks! 🙌
6 changes: 3 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ let package = Package(
.library(name: "PostgresKit", targets: ["PostgresKit"]),
],
dependencies: [
.package(url: "https://github.com/vapor/postgres-nio.git", from: "1.0.0-beta.2"),
.package(url: "https://github.com/vapor/sql-kit.git", from: "3.0.0-beta.2"),
.package(url: "https://github.com/vapor/async-kit.git", from: "1.0.0-beta.2"),
.package(url: "https://github.com/vapor/postgres-nio.git", from: "1.0.0"),
.package(url: "https://github.com/vapor/sql-kit.git", from: "3.0.0"),
.package(url: "https://github.com/vapor/async-kit.git", from: "1.0.0"),
],
targets: [
.target(name: "PostgresKit", dependencies: [
Expand Down
165 changes: 141 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,141 @@
<p align="center">
<img
src="https://user-images.githubusercontent.com/1342803/59063319-d190f500-8875-11e9-8fe6-16197dd56d0f.png"
height="64"
alt="PostgresKit"
>
<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/59063319-d190f500-8875-11e9-8fe6-16197dd56d0f.png" height="64" alt="PostgresKit">
<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/postgres-kit/actions">
<img src="https://github.com/vapor/postgres-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>

🐘 Non-blocking, event-driven Swift client for PostgreSQL.

### Major Releases

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

|Version|NIO|Swift|SPM|
|-|-|-|-|
|2.0|2.0|5.2+|`from: "2.0.0"`|
|1.0|1.0|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/postgres-kit.git", from: ...)
```

### Supported Platforms

PostgresKit supports the following platforms:

- Ubuntu 16.04+
- macOS 10.15+

## Overview

PostgresKit is a PostgreSQL client library built on [SQLKit](https://github.com/vapor/sql-kit). It supports building and serializing Postgres-dialect SQL queries. PostgresKit uses [PostgresNIO](https://github.com/vapor/postgres-nio) to connect and communicate with the database server asynchronously. [AsyncKit](https://github.com/vapor/async-kit) is used to provide connection pooling.

### Configuration

Database connection options and credentials are specified using a `PostgresConfiguration` struct.

```swift
import PostgresKit

let configuration = PostgresConfiguration(
hostname: "localhost",
username: "vapor_username",
password: "vapor_password",
database: "vapor_database"
)
```

URL string based configuration is also supported.

```swift
guard let configuration = PostgresConfiguration(url: "postgres://...") else {
...
}
```

To connect via unix-domain sockets, use `unixDomainSocketPath` instead of `hostname` and `port`.

```swift
let configuration = PostgresConfiguration(
unixDomainSocketPath: "/path/to/socket",
username: "vapor_username",
password: "vapor_password",
database: "vapor_database"
)
```

### Connection Pool

Once you have a `PostgresConfiguration`, you can use it to create a connection source and pool.

```swift
let eventLoopGroup: EventLoopGroup = ...
defer { try! eventLoopGroup.syncShutdown() }

let pools = EventLoopGroupConnectionPool(
source: PostgresConnectionSource(configuration: configuration),
on: eventLoopGroup
)
defer { pools.shutdown() }
```

First create a `PostgresConnectionSource` using the configuration struct. This type is responsible for creating new connections to your database server as needed.

Next, use the connection source to create an `EventLoopGroupConnectionPool`. You will also need to pass an `EventLoopGroup`. For more information on creating an `EventLoopGroup`, visit SwiftNIO's [documentation](https://apple.github.io/swift-nio/docs/current/NIO/index.html). Make sure to shutdown the connection pool before it deinitializes.

`EventLoopGroupConnectionPool` is a collection of pools for each event loop. When using `EventLoopGroupConnectionPool` directly, random event loops will be chosen as needed.

```swift
pools.withConnection { conn
print(conn) // PostgresConnection on randomly chosen event loop
}
```

To get a pool for a specific event loop, use `pool(for:)`. This returns an `EventLoopConnectionPool`.

```swift
let eventLoop: EventLoop = ...
let pool = pools.pool(for: eventLoop)

pool.withConnection { conn
print(conn) // PostgresConnection on eventLoop
}
```

### PostgresDatabase

Both `EventLoopGroupConnectionPool` and `EventLoopConnectionPool` can be used to create instances of `PostgresDatabase`.

```swift
let postgres = pool.database(logger: ...) // PostgresDatabase
let rows = try postgres.simpleQuery("SELECT version();").wait()
```

Visit [PostgresNIO's docs](https://github.com/vapor/postgres-nio) for more information on using `PostgresDatabase`.

### SQLDatabase

A `PostgresDatabase` can be used to create an instance of `SQLDatabase`.

```swift
let sql = postgres.sql() // SQLDatabase
let planets = try sql.select().column("*").from("planets").all().wait()
```

Visit [SQLKit's docs](https://github.com/vapor/sql-kit) for more information on using `SQLDatabase`.
2 changes: 2 additions & 0 deletions Sources/PostgresKit/ConnectionPool+Postgres.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ extension EventLoopGroupConnectionPool where Source == PostgresConnectionSource
}
}

// MARK: Private

private struct _EventLoopGroupConnectionPoolPostgresDatabase {
let pool: EventLoopGroupConnectionPool<PostgresConnectionSource>
let logger: Logger
Expand Down
2 changes: 2 additions & 0 deletions Sources/PostgresKit/PostgresClient+SQL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ extension PostgresDatabase {
}
}

// MARK: Private

private struct _PostgresSQLDatabase {
let database: PostgresDatabase
let encoder: PostgresDataEncoder
Expand Down
2 changes: 1 addition & 1 deletion Sources/PostgresKit/PostgresColumnType.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import SQLKit

/// PostgreSQL specific `SQLDataType`.
/// Postgres-specific column types.
public struct PostgresColumnType: SQLExpression, Equatable {
public static var blob: PostgresColumnType {
return .varbit
Expand Down
44 changes: 21 additions & 23 deletions Sources/PostgresKit/PostgresConfiguration.swift
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
@_exported import Foundation
@_exported import struct Foundation.URL

public struct PostgresConfiguration {
public let address: () throws -> SocketAddress
public let username: String
public let password: String
public let database: String?
public let tlsConfiguration: TLSConfiguration?
public var address: () throws -> SocketAddress
public var username: String
public var password: String?
public var database: String?
public var tlsConfiguration: TLSConfiguration?

public let encoder: PostgresDataEncoder
public let decoder: PostgresDataDecoder
/// Optional `search_path` to set on new connections.
public var searchPath: [String]?

internal var _hostname: String?


public init?(url: String) {
guard let url = URL(string: url) else {
return nil
}
self.init(url: url)
}

public init?(url: URL) {
guard url.scheme?.hasPrefix("postgres") == true else {
Expand All @@ -19,9 +27,7 @@ public struct PostgresConfiguration {
guard let username = url.user else {
return nil
}
guard let password = url.password else {
return nil
}
let password = url.password
guard let hostname = url.host else {
return nil
}
Expand All @@ -47,10 +53,8 @@ public struct PostgresConfiguration {
public init(
unixDomainSocketPath: String,
username: String,
password: String,
database: String,
encoder: PostgresDataEncoder = PostgresDataEncoder(),
decoder: PostgresDataDecoder = PostgresDataDecoder()
password: String? = nil,
database: String? = nil
) {
self.address = {
return try SocketAddress.init(unixDomainSocketPath: unixDomainSocketPath)
Expand All @@ -60,19 +64,15 @@ public struct PostgresConfiguration {
self.database = database
self.tlsConfiguration = nil
self._hostname = nil
self.encoder = encoder
self.decoder = decoder
}

public init(
hostname: String,
port: Int = 5432,
username: String,
password: String,
password: String? = nil,
database: String? = nil,
tlsConfiguration: TLSConfiguration? = nil,
encoder: PostgresDataEncoder = PostgresDataEncoder(),
decoder: PostgresDataDecoder = PostgresDataDecoder()
tlsConfiguration: TLSConfiguration? = nil
) {
self.address = {
return try SocketAddress.makeAddressResolvingHost(hostname, port: port)
Expand All @@ -82,7 +82,5 @@ public struct PostgresConfiguration {
self.password = password
self.tlsConfiguration = tlsConfiguration
self._hostname = hostname
self.encoder = encoder
self.decoder = decoder
}
}
11 changes: 10 additions & 1 deletion Sources/PostgresKit/PostgresConnectionSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public struct PostgresConnectionSource: ConnectionPoolSource {
return PostgresConnection.connect(
to: address,
tlsConfiguration: self.configuration.tlsConfiguration,
serverHostname: self.configuration._hostname,
logger: .init(label: "codes.vapor.postgres"),
on: eventLoop
).flatMap { conn in
Expand All @@ -26,7 +27,15 @@ public struct PostgresConnectionSource: ConnectionPoolSource {
database: self.configuration.database,
password: self.configuration.password,
logger: logger
).flatMapErrorThrowing { error in
).flatMap {
if let searchPath = self.configuration.searchPath {
let string = searchPath.map { "\"" + $0 + "\"" }.joined(separator: ", ")
return conn.simpleQuery("SET search_path = \(string)")
.map { _ in }
} else {
return eventLoop.makeSucceededFuture(())
}
}.flatMapErrorThrowing { error in
_ = conn.close()
throw error
}.map { conn }
Expand Down
22 changes: 14 additions & 8 deletions Sources/PostgresKit/PostgresDataDecoder.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
import Foundation

struct DecoderUnwrapper: Decodable {
let decoder: Decoder
init(from decoder: Decoder) {
self.decoder = decoder
}
}

public final class PostgresDataDecoder {
public let jsonDecoder: JSONDecoder

Expand All @@ -18,7 +11,13 @@ public final class PostgresDataDecoder {
where T: Decodable
{
if let convertible = T.self as? PostgresDataConvertible.Type {
return convertible.init(postgresData: data)! as! T
guard let value = convertible.init(postgresData: data) else {
throw DecodingError.typeMismatch(T.self, DecodingError.Context.init(
codingPath: [],
debugDescription: "Could not convert to \(T.self): \(data)"
))
}
return value as! T
} else {
return try T.init(from: _Decoder(data: data, json: self.jsonDecoder))
}
Expand Down Expand Up @@ -147,3 +146,10 @@ public final class PostgresDataDecoder {
}
}
}

struct DecoderUnwrapper: Decodable {
let decoder: Decoder
init(from decoder: Decoder) {
self.decoder = decoder
}
}
2 changes: 2 additions & 0 deletions Sources/PostgresKit/PostgresRow+SQL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ extension PostgresRow {
}
}

// MARK: Private

private struct _PostgreSQLRow: SQLRow {
let row: PostgresRow
let decoder: PostgresDataDecoder
Expand Down
Loading

0 comments on commit 152f69b

Please sign in to comment.