Skip to content

Commit 152f69b

Browse files
authored
2.0.0 gm (vapor#182)
* 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 vapor#9 * optional password * fix decoder force unwrap vapor#175 * pass server hostname, fixes vapor#178 * array type test * test fluent gm branch * master + import fix
1 parent b465e9e commit 152f69b

15 files changed

+269
-126
lines changed

.github/contributing.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Contributing to PostgresKit
2+
3+
👋 Welcome to the Vapor team!
4+
5+
## Docker
6+
7+
This package includes a `docker-compose` file you can use for spinning up test databases with test credentials.
8+
9+
```sh
10+
$ docker-compose up psql-11
11+
```
12+
13+
## Testing
14+
15+
Once in Xcode, select the `postgres-kit` scheme and use `CMD+U` to run the tests.
16+
17+
You can also test via the CLI using `swift test`.
18+
19+
If you are fixing a single GitHub issue in particular, you can add a test named `testGH<issue number>` to ensure
20+
that your fix is working. This will also help prevent regression.
21+
22+
## SemVer
23+
24+
Vapor follows [SemVer](https://semver.org). This means that any changes to the source code that can cause
25+
existing code to stop compiling _must_ wait until the next major version to be included.
26+
27+
Code that is only additive and will not break any existing code can be included in the next minor release.
28+
29+
----------
30+
31+
Join us on Discord if you have any questions: [discord.gg/vapor](https://discord.gg/vapor).
32+
33+
&mdash; Thanks! 🙌

Package.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ let package = Package(
1010
.library(name: "PostgresKit", targets: ["PostgresKit"]),
1111
],
1212
dependencies: [
13-
.package(url: "https://github.com/vapor/postgres-nio.git", from: "1.0.0-beta.2"),
14-
.package(url: "https://github.com/vapor/sql-kit.git", from: "3.0.0-beta.2"),
15-
.package(url: "https://github.com/vapor/async-kit.git", from: "1.0.0-beta.2"),
13+
.package(url: "https://github.com/vapor/postgres-nio.git", from: "1.0.0"),
14+
.package(url: "https://github.com/vapor/sql-kit.git", from: "3.0.0"),
15+
.package(url: "https://github.com/vapor/async-kit.git", from: "1.0.0"),
1616
],
1717
targets: [
1818
.target(name: "PostgresKit", dependencies: [

README.md

Lines changed: 141 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,141 @@
1-
<p align="center">
2-
<img
3-
src="https://user-images.githubusercontent.com/1342803/59063319-d190f500-8875-11e9-8fe6-16197dd56d0f.png"
4-
height="64"
5-
alt="PostgresKit"
6-
>
7-
<br>
8-
<br>
9-
<a href="https://docs.vapor.codes/4.0/">
10-
<img src="http://img.shields.io/badge/read_the-docs-2196f3.svg" alt="Documentation">
11-
</a>
12-
<a href="https://discord.gg/vapor">
13-
<img src="https://img.shields.io/discord/431917998102675485.svg" alt="Team Chat">
14-
</a>
15-
<a href="LICENSE">
16-
<img src="http://img.shields.io/badge/license-MIT-brightgreen.svg" alt="MIT License">
17-
</a>
18-
<a href="https://github.com/vapor/sql-kit/actions">
19-
<img src="https://github.com/vapor/sql-kit/workflows/test/badge.svg" alt="Continuous Integration">
20-
</a>
21-
<a href="https://swift.org">
22-
<img src="http://img.shields.io/badge/swift-5.2-brightgreen.svg" alt="Swift 5.2">
23-
</a>
24-
</p>
1+
<img src="https://user-images.githubusercontent.com/1342803/59063319-d190f500-8875-11e9-8fe6-16197dd56d0f.png" height="64" alt="PostgresKit">
2+
<br>
3+
<a href="https://docs.vapor.codes/4.0/">
4+
<img src="http://img.shields.io/badge/read_the-docs-2196f3.svg" alt="Documentation">
5+
</a>
6+
<a href="https://discord.gg/vapor">
7+
<img src="https://img.shields.io/discord/431917998102675485.svg" alt="Team Chat">
8+
</a>
9+
<a href="LICENSE">
10+
<img src="http://img.shields.io/badge/license-MIT-brightgreen.svg" alt="MIT License">
11+
</a>
12+
<a href="https://github.com/vapor/postgres-kit/actions">
13+
<img src="https://github.com/vapor/postgres-kit/workflows/test/badge.svg" alt="Continuous Integration">
14+
</a>
15+
<a href="https://swift.org">
16+
<img src="http://img.shields.io/badge/swift-5.2-brightgreen.svg" alt="Swift 5.2">
17+
</a>
18+
<br>
19+
<br>
20+
21+
🐘 Non-blocking, event-driven Swift client for PostgreSQL.
22+
23+
### Major Releases
24+
25+
The table below shows a list of PostgresKit major releases alongside their compatible NIO and Swift versions.
26+
27+
|Version|NIO|Swift|SPM|
28+
|-|-|-|-|
29+
|2.0|2.0|5.2+|`from: "2.0.0"`|
30+
|1.0|1.0|4.0+|`from: "1.0.0"`|
31+
32+
Use the SPM string to easily include the dependendency in your `Package.swift` file.
33+
34+
```swift
35+
.package(url: "https://github.com/vapor/postgres-kit.git", from: ...)
36+
```
37+
38+
### Supported Platforms
39+
40+
PostgresKit supports the following platforms:
41+
42+
- Ubuntu 16.04+
43+
- macOS 10.15+
44+
45+
## Overview
46+
47+
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.
48+
49+
### Configuration
50+
51+
Database connection options and credentials are specified using a `PostgresConfiguration` struct.
52+
53+
```swift
54+
import PostgresKit
55+
56+
let configuration = PostgresConfiguration(
57+
hostname: "localhost",
58+
username: "vapor_username",
59+
password: "vapor_password",
60+
database: "vapor_database"
61+
)
62+
```
63+
64+
URL string based configuration is also supported.
65+
66+
```swift
67+
guard let configuration = PostgresConfiguration(url: "postgres://...") else {
68+
...
69+
}
70+
```
71+
72+
To connect via unix-domain sockets, use `unixDomainSocketPath` instead of `hostname` and `port`.
73+
74+
```swift
75+
let configuration = PostgresConfiguration(
76+
unixDomainSocketPath: "/path/to/socket",
77+
username: "vapor_username",
78+
password: "vapor_password",
79+
database: "vapor_database"
80+
)
81+
```
82+
83+
### Connection Pool
84+
85+
Once you have a `PostgresConfiguration`, you can use it to create a connection source and pool.
86+
87+
```swift
88+
let eventLoopGroup: EventLoopGroup = ...
89+
defer { try! eventLoopGroup.syncShutdown() }
90+
91+
let pools = EventLoopGroupConnectionPool(
92+
source: PostgresConnectionSource(configuration: configuration),
93+
on: eventLoopGroup
94+
)
95+
defer { pools.shutdown() }
96+
```
97+
98+
First create a `PostgresConnectionSource` using the configuration struct. This type is responsible for creating new connections to your database server as needed.
99+
100+
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.
101+
102+
`EventLoopGroupConnectionPool` is a collection of pools for each event loop. When using `EventLoopGroupConnectionPool` directly, random event loops will be chosen as needed.
103+
104+
```swift
105+
pools.withConnection { conn
106+
print(conn) // PostgresConnection on randomly chosen event loop
107+
}
108+
```
109+
110+
To get a pool for a specific event loop, use `pool(for:)`. This returns an `EventLoopConnectionPool`.
111+
112+
```swift
113+
let eventLoop: EventLoop = ...
114+
let pool = pools.pool(for: eventLoop)
115+
116+
pool.withConnection { conn
117+
print(conn) // PostgresConnection on eventLoop
118+
}
119+
```
120+
121+
### PostgresDatabase
122+
123+
Both `EventLoopGroupConnectionPool` and `EventLoopConnectionPool` can be used to create instances of `PostgresDatabase`.
124+
125+
```swift
126+
let postgres = pool.database(logger: ...) // PostgresDatabase
127+
let rows = try postgres.simpleQuery("SELECT version();").wait()
128+
```
129+
130+
Visit [PostgresNIO's docs](https://github.com/vapor/postgres-nio) for more information on using `PostgresDatabase`.
131+
132+
### SQLDatabase
133+
134+
A `PostgresDatabase` can be used to create an instance of `SQLDatabase`.
135+
136+
```swift
137+
let sql = postgres.sql() // SQLDatabase
138+
let planets = try sql.select().column("*").from("planets").all().wait()
139+
```
140+
141+
Visit [SQLKit's docs](https://github.com/vapor/sql-kit) for more information on using `SQLDatabase`.

Sources/PostgresKit/ConnectionPool+Postgres.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ extension EventLoopGroupConnectionPool where Source == PostgresConnectionSource
44
}
55
}
66

7+
// MARK: Private
8+
79
private struct _EventLoopGroupConnectionPoolPostgresDatabase {
810
let pool: EventLoopGroupConnectionPool<PostgresConnectionSource>
911
let logger: Logger

Sources/PostgresKit/PostgresClient+SQL.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ extension PostgresDatabase {
1111
}
1212
}
1313

14+
// MARK: Private
15+
1416
private struct _PostgresSQLDatabase {
1517
let database: PostgresDatabase
1618
let encoder: PostgresDataEncoder

Sources/PostgresKit/PostgresColumnType.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import SQLKit
22

3-
/// PostgreSQL specific `SQLDataType`.
3+
/// Postgres-specific column types.
44
public struct PostgresColumnType: SQLExpression, Equatable {
55
public static var blob: PostgresColumnType {
66
return .varbit
Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
1-
@_exported import Foundation
1+
@_exported import struct Foundation.URL
22

33
public struct PostgresConfiguration {
4-
public let address: () throws -> SocketAddress
5-
public let username: String
6-
public let password: String
7-
public let database: String?
8-
public let tlsConfiguration: TLSConfiguration?
4+
public var address: () throws -> SocketAddress
5+
public var username: String
6+
public var password: String?
7+
public var database: String?
8+
public var tlsConfiguration: TLSConfiguration?
99

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

1313
internal var _hostname: String?
14+
15+
16+
public init?(url: String) {
17+
guard let url = URL(string: url) else {
18+
return nil
19+
}
20+
self.init(url: url)
21+
}
1422

1523
public init?(url: URL) {
1624
guard url.scheme?.hasPrefix("postgres") == true else {
@@ -19,9 +27,7 @@ public struct PostgresConfiguration {
1927
guard let username = url.user else {
2028
return nil
2129
}
22-
guard let password = url.password else {
23-
return nil
24-
}
30+
let password = url.password
2531
guard let hostname = url.host else {
2632
return nil
2733
}
@@ -47,10 +53,8 @@ public struct PostgresConfiguration {
4753
public init(
4854
unixDomainSocketPath: String,
4955
username: String,
50-
password: String,
51-
database: String,
52-
encoder: PostgresDataEncoder = PostgresDataEncoder(),
53-
decoder: PostgresDataDecoder = PostgresDataDecoder()
56+
password: String? = nil,
57+
database: String? = nil
5458
) {
5559
self.address = {
5660
return try SocketAddress.init(unixDomainSocketPath: unixDomainSocketPath)
@@ -60,19 +64,15 @@ public struct PostgresConfiguration {
6064
self.database = database
6165
self.tlsConfiguration = nil
6266
self._hostname = nil
63-
self.encoder = encoder
64-
self.decoder = decoder
6567
}
6668

6769
public init(
6870
hostname: String,
6971
port: Int = 5432,
7072
username: String,
71-
password: String,
73+
password: String? = nil,
7274
database: String? = nil,
73-
tlsConfiguration: TLSConfiguration? = nil,
74-
encoder: PostgresDataEncoder = PostgresDataEncoder(),
75-
decoder: PostgresDataDecoder = PostgresDataDecoder()
75+
tlsConfiguration: TLSConfiguration? = nil
7676
) {
7777
self.address = {
7878
return try SocketAddress.makeAddressResolvingHost(hostname, port: port)
@@ -82,7 +82,5 @@ public struct PostgresConfiguration {
8282
self.password = password
8383
self.tlsConfiguration = tlsConfiguration
8484
self._hostname = hostname
85-
self.encoder = encoder
86-
self.decoder = decoder
8785
}
8886
}

Sources/PostgresKit/PostgresConnectionSource.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public struct PostgresConnectionSource: ConnectionPoolSource {
1818
return PostgresConnection.connect(
1919
to: address,
2020
tlsConfiguration: self.configuration.tlsConfiguration,
21+
serverHostname: self.configuration._hostname,
2122
logger: .init(label: "codes.vapor.postgres"),
2223
on: eventLoop
2324
).flatMap { conn in
@@ -26,7 +27,15 @@ public struct PostgresConnectionSource: ConnectionPoolSource {
2627
database: self.configuration.database,
2728
password: self.configuration.password,
2829
logger: logger
29-
).flatMapErrorThrowing { error in
30+
).flatMap {
31+
if let searchPath = self.configuration.searchPath {
32+
let string = searchPath.map { "\"" + $0 + "\"" }.joined(separator: ", ")
33+
return conn.simpleQuery("SET search_path = \(string)")
34+
.map { _ in }
35+
} else {
36+
return eventLoop.makeSucceededFuture(())
37+
}
38+
}.flatMapErrorThrowing { error in
3039
_ = conn.close()
3140
throw error
3241
}.map { conn }

Sources/PostgresKit/PostgresDataDecoder.swift

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
import Foundation
22

3-
struct DecoderUnwrapper: Decodable {
4-
let decoder: Decoder
5-
init(from decoder: Decoder) {
6-
self.decoder = decoder
7-
}
8-
}
9-
103
public final class PostgresDataDecoder {
114
public let jsonDecoder: JSONDecoder
125

@@ -18,7 +11,13 @@ public final class PostgresDataDecoder {
1811
where T: Decodable
1912
{
2013
if let convertible = T.self as? PostgresDataConvertible.Type {
21-
return convertible.init(postgresData: data)! as! T
14+
guard let value = convertible.init(postgresData: data) else {
15+
throw DecodingError.typeMismatch(T.self, DecodingError.Context.init(
16+
codingPath: [],
17+
debugDescription: "Could not convert to \(T.self): \(data)"
18+
))
19+
}
20+
return value as! T
2221
} else {
2322
return try T.init(from: _Decoder(data: data, json: self.jsonDecoder))
2423
}
@@ -147,3 +146,10 @@ public final class PostgresDataDecoder {
147146
}
148147
}
149148
}
149+
150+
struct DecoderUnwrapper: Decodable {
151+
let decoder: Decoder
152+
init(from decoder: Decoder) {
153+
self.decoder = decoder
154+
}
155+
}

Sources/PostgresKit/PostgresRow+SQL.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ extension PostgresRow {
44
}
55
}
66

7+
// MARK: Private
8+
79
private struct _PostgreSQLRow: SQLRow {
810
let row: PostgresRow
911
let decoder: PostgresDataDecoder

0 commit comments

Comments
 (0)