-🐘 Non-blocking, event-driven Swift client for PostgreSQL.
+PostgresKit is an [SQLKit] driver for PostgreSQL clients.
+
+## Overview
+
+PostgresKit supports building and serializing Postgres-dialect SQL queries using [SQLKit]'s API. PostgresKit uses [PostgresNIO] to connect and communicate with the database server asynchronously. [AsyncKit] is used to provide connection pooling.
+
+> Important: It is strongly recommended that users who leverage PostgresKit directly (e.g. absent the Fluent ORM layer) take advantage of PostgresNIO's [PostgresClient] API for connection management rather than relying upon the legacy AsyncKit API.
### Usage
-Use the SPM string to easily include the dependendency in your `Package.swift` file.
+Reference this package in your `Package.swift` to include it in your project.
```swift
.package(url: "https://github.com/vapor/postgres-kit.git", from: "2.0.0")
@@ -33,26 +35,14 @@ PostgresKit supports the following platforms:
- Ubuntu 20.04+
- macOS 10.15+
-## Overview
-
-PostgresKit is an [SQLKit] driver for PostgreSQL clients. It supports building and serializing Postgres-dialect SQL queries. PostgresKit uses [PostgresNIO] to connect and communicate with the database server asynchronously. [AsyncKit](https://github.com/vapor/async-kit) is used to provide connection pooling.
-
-> [!IMPORTANT]
-> It is strongly recommended that users who leverage PostgresKit directly (e.g. absent the Fluent ORM layer) take advantage of PostgresNIO's [PostgresClient] API for connection management rather than relying upon the legacy AsyncKit API.
-
-[SQLKit]: https://github.com/vapor/sql-kit
-[PostgresNIO]: https://github.com/vapor/postgres-nio
-[AsyncKit]: https://github.com/vapor/async-kit
-[PostgresClient]: https://api.vapor.codes/postgresnio/documentation/postgresnio/postgresclient
-
### Configuration
-Database connection options and credentials are specified using a `PostgresConfiguration` struct.
+Database connection options and credentials are specified using a ``SQLPostgresConfiguration`` struct.
```swift
import PostgresKit
-let configuration = PostgresConfiguration(
+let configuration = SQLPostgresConfiguration(
hostname: "localhost",
username: "vapor_username",
password: "vapor_password",
@@ -60,15 +50,15 @@ let configuration = PostgresConfiguration(
)
```
-URL string based configuration is also supported.
+URL-based configuration is also supported.
```swift
-guard let configuration = PostgresConfiguration(url: "postgres://...") else {
+guard let configuration = SQLPostgresConfiguration(url: "postgres://...") else {
...
}
```
-To connect via unix-domain sockets, use `unixDomainSocketPath` instead of `hostname` and `port`.
+To connect via unix-domain sockets, use ``SQLPostgresConfiguration/init(unixDomainSocketPath:username:password:database:)`` instead of ``SQLPostgresConfiguration/init(hostname:port:username:password:database:tls:)``.
```swift
let configuration = PostgresConfiguration(
@@ -81,22 +71,22 @@ let configuration = PostgresConfiguration(
### Connection Pool
-Once you have a `PostgresConfiguration`, you can use it to create a connection source and pool.
+Once you have a ``SQLPostgresConfiguration``, you can use it to create a connection source and pool.
```swift
-let eventLoopGroup: EventLoopGroup = ...
-defer { try! eventLoopGroup.syncShutdown() }
-
+let eventLoopGroup: EventLoopGroup = NIOSingletons.posixEventLoopGroup
let pools = EventLoopGroupConnectionPool(
source: PostgresConnectionSource(configuration: configuration),
on: eventLoopGroup
)
-defer { pools.shutdown() }
+
+// When you're done:
+try await pools.shutdownAsync()
```
-First create a `PostgresConnectionSource` using the configuration struct. This type is responsible for creating new connections to your database server as needed.
+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.
+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]. 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.
@@ -126,7 +116,7 @@ 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`.
+Visit [PostgresNIO's docs] for more information on using `PostgresDatabase`.
### SQLDatabase
@@ -137,4 +127,12 @@ 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`.
+Visit [SQLKit's docs] for more information on using `SQLDatabase`.
+
+[SQLKit]: https://github.com/vapor/sql-kit
+[SQLKit's docs]: https://api.vapor.codes/sqlkit/documentation/sqlkit
+[PostgresNIO]: https://github.com/vapor/postgres-nio
+[PostgresNIO's docs]: https://api.vapor.codes/postgresnio/documentation/postgresnio
+[AsyncKit]: https://github.com/vapor/async-kit
+[PostgresClient]: https://api.vapor.codes/postgresnio/documentation/postgresnio/postgresclient
+[SwiftNIO's documentation]: https://swiftpackageindex.com/apple/swift-nio/documentation/nio
diff --git a/Sources/PostgresKit/Docs.docc/PostgresKit.md b/Sources/PostgresKit/Docs.docc/PostgresKit.md
index 4b86941e..47bef7fa 100644
--- a/Sources/PostgresKit/Docs.docc/PostgresKit.md
+++ b/Sources/PostgresKit/Docs.docc/PostgresKit.md
@@ -4,29 +4,127 @@
@TitleHeading(Package)
}
-PostgresKit is a library providing an SQLKit driver for PostgresNIO.
+PostgresKit is an [SQLKit] driver for PostgreSQL clients.
## Overview
-This package provides the "foundational" level of support for using [Fluent] with PostgreSQL by implementing the requirements of an [SQLKit] driver. It is responsible for:
+PostgresKit supports building and serializing Postgres-dialect SQL queries using [SQLKit]'s API. PostgresKit uses [PostgresNIO] to connect and communicate with the database server asynchronously. [AsyncKit] is used to provide connection pooling.
-- Managing the underlying PostgreSQL library ([PostgresNIO]),
-- Providing a two-way bridge between PostgresNIO and SQLKit's generic data and metadata formats, and
-- Presenting an interface for establishing, managing, and interacting with database connections via [AsyncKit].
+> Important: It is strongly recommended that users who leverage PostgresKit directly (e.g. absent the Fluent ORM layer) take advantage of PostgresNIO's [PostgresClient] API for connection management rather than relying upon the legacy AsyncKit API.
-> Important: It is strongly recommended that users who leverage PostgresKit directly (e.g. absent the Fluent ORM layer) take advantage of PostgresNIO's [PostgresClient] API for connection management rather than relying upon the legacy AsyncKit-based support.
+### Usage
-> Tip: A FluentKit driver for PostgreSQL is provided by the [FluentPostgresDriver] package.
+Reference this package in your `Package.swift` to include it in your project.
-## Version Support
+```swift
+.package(url: "https://github.com/vapor/postgres-kit.git", from: "2.0.0")
+```
-This package uses [PostgresNIO] for all underlying database interactions. It is compatible with all versions of PostgreSQL and all platforms supported by that package.
+### Supported Platforms
-> Caution: There is one exception to the above at the time of this writing: This package requires Swift 5.8 or newer, whereas PostgresNIO continues to support Swift 5.6.
+PostgresKit supports the following platforms:
-[SQLKit]: https://swiftpackageindex.com/vapor/sql-kit
-[PostgresNIO]: https://swiftpackageindex.com/vapor/postgres-nio
-[Fluent]: https://swiftpackageindex.com/vapor/fluent-kit
-[FluentPostgresDriver]: https://swiftpackageindex.com/vapor/fluent-postgres-driver
-[AsyncKit]: https://swiftpackageindex.com/vapor/async-kit
+- Ubuntu 20.04+
+- macOS 10.15+
+
+### Configuration
+
+Database connection options and credentials are specified using a ``SQLPostgresConfiguration`` struct.
+
+```swift
+import PostgresKit
+
+let configuration = SQLPostgresConfiguration(
+ hostname: "localhost",
+ username: "vapor_username",
+ password: "vapor_password",
+ database: "vapor_database"
+)
+```
+
+URL-based configuration is also supported.
+
+```swift
+guard let configuration = SQLPostgresConfiguration(url: "postgres://...") else {
+ ...
+}
+```
+
+To connect via unix-domain sockets, use ``SQLPostgresConfiguration/init(unixDomainSocketPath:username:password:database:)`` instead of ``SQLPostgresConfiguration/init(hostname:port:username:password:database:tls:)``.
+
+```swift
+let configuration = PostgresConfiguration(
+ unixDomainSocketPath: "/path/to/socket",
+ username: "vapor_username",
+ password: "vapor_password",
+ database: "vapor_database"
+)
+```
+
+### Connection Pool
+
+Once you have a ``SQLPostgresConfiguration``, you can use it to create a connection source and pool.
+
+```swift
+let eventLoopGroup: EventLoopGroup = NIOSingletons.posixEventLoopGroup
+let pools = EventLoopGroupConnectionPool(
+ source: PostgresConnectionSource(configuration: configuration),
+ on: eventLoopGroup
+)
+
+// When you're done:
+try await pools.shutdownAsync()
+```
+
+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]. 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] 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] for more information on using `SQLDatabase`.
+
+[SQLKit]: https://github.com/vapor/sql-kit
+[SQLKit's docs]: https://api.vapor.codes/sqlkit/documentation/sqlkit
+[PostgresNIO]: https://github.com/vapor/postgres-nio
+[PostgresNIO's docs]: https://api.vapor.codes/postgresnio/documentation/postgresnio
+[AsyncKit]: https://github.com/vapor/async-kit
[PostgresClient]: https://api.vapor.codes/postgresnio/documentation/postgresnio/postgresclient
+[SwiftNIO's documentation]: https://swiftpackageindex.com/apple/swift-nio/documentation/nio
diff --git a/Sources/PostgresKit/Docs.docc/Resources/vapor-postgreskit-logo.svg b/Sources/PostgresKit/Docs.docc/Resources/vapor-postgreskit-logo.svg
index 577997a4..6482a7d0 100644
--- a/Sources/PostgresKit/Docs.docc/Resources/vapor-postgreskit-logo.svg
+++ b/Sources/PostgresKit/Docs.docc/Resources/vapor-postgreskit-logo.svg
@@ -1,21 +1,25 @@
diff --git a/Sources/PostgresKit/Docs.docc/theme-settings.json b/Sources/PostgresKit/Docs.docc/theme-settings.json
index d78a16fd..52bf00bc 100644
--- a/Sources/PostgresKit/Docs.docc/theme-settings.json
+++ b/Sources/PostgresKit/Docs.docc/theme-settings.json
@@ -15,7 +15,7 @@
"logo-shape": { "dark": "#000", "light": "#fff" },
"fill": { "dark": "#000", "light": "#fff" }
},
- "icons": { "technology": "/postgreskit/images/vapor-postgreskit-logo.svg" }
+ "icons": { "technology": "/postgreskit/images/PostgresKit/vapor-postgreskit-logo.svg" }
},
"features": {
"quickNavigation": { "enable": true },
diff --git a/Sources/PostgresKit/PostgresDataTranslation.swift b/Sources/PostgresKit/PostgresDataTranslation.swift
index 3e04d790..eb7053cd 100644
--- a/Sources/PostgresKit/PostgresDataTranslation.swift
+++ b/Sources/PostgresKit/PostgresDataTranslation.swift
@@ -1,27 +1,27 @@
import Foundation
import PostgresNIO
-/// Quick and dirty ``CodingKey``, borrowed from FluentKit. If ``CodingKeyRepresentable`` wasn't broken by design
+/// Quick and dirty `CodingKey`, borrowed from FluentKit. If `CodingKeyRepresentable` wasn't broken by design
/// (specifically, it can't be back-deployed before macOS 12.3 etc., even though it was introduced in Swift 5.6),
/// we'd use that instead.
-fileprivate struct SomeCodingKey: CodingKey, Hashable {
+private struct SomeCodingKey: CodingKey, Hashable {
let stringValue: String, intValue: Int?
init(stringValue: String) { (self.stringValue, self.intValue) = (stringValue, Int(stringValue)) }
init(intValue: Int) { (self.stringValue, self.intValue) = ("\(intValue)", intValue) }
}
-private extension PostgresCell {
- var codingKey: any CodingKey {
+extension PostgresCell {
+ fileprivate var codingKey: any CodingKey {
PostgresKit.SomeCodingKey(stringValue: !self.columnName.isEmpty ? "\(self.columnName) (\(self.columnIndex))" : "\(self.columnIndex)")
}
}
/// Sidestep problems with URL coding behavior by making it conform directly to Postgres coding.
-extension Foundation.URL: PostgresNIO.PostgresNonThrowingEncodable, PostgresNIO.PostgresDecodable {
+extension URL {
public static var psqlType: PostgresDataType {
String.psqlType
}
-
+
public static var psqlFormat: PostgresFormat {
String.psqlFormat
}
@@ -42,7 +42,7 @@ extension Foundation.URL: PostgresNIO.PostgresNonThrowingEncodable, PostgresNIO.
context: PostgresDecodingContext
) throws {
let string = try String(from: &buffer, type: type, format: format, context: context)
-
+
if let url = URL(string: string) {
self = url
}
@@ -55,16 +55,23 @@ extension Foundation.URL: PostgresNIO.PostgresNonThrowingEncodable, PostgresNIO.
}
}
+#if compiler(>=6.0)
+extension URL: @retroactive PostgresNonThrowingEncodable, @retroactive PostgresDecodable {}
+#else
+extension Foundation.URL: PostgresNIO.PostgresNonThrowingEncodable, PostgresNIO.PostgresDecodable {}
+#endif
+
struct PostgresDataTranslation {
- /// This typealias serves to limit the deprecation noise caused by ``PostgresDataConvertible`` to a single
+ /// This typealias serves to limit the deprecation noise caused by `PostgresDataConvertible` to a single
/// warning, down from what would otherwise be a minimum of two. It has no other purpose.
fileprivate typealias PostgresLegacyDataConvertible = PostgresDataConvertible
-
+
static func decode(
_: T.Type = T.self,
from cell: PostgresCell,
in context: PostgresDecodingContext,
- file: String = #fileID, line: Int = #line
+ file: String = #fileID,
+ line: Int = #line
) throws -> T {
try self.decode(
codingPath: [cell.codingKey],
@@ -72,18 +79,21 @@ struct PostgresDataTranslation {
T.self,
from: cell,
in: context,
- file: file, line: line
+ file: file,
+ line: line
)
}
-
+
fileprivate static func decode(
- codingPath: [any CodingKey], userInfo: [CodingUserInfoKey: Any],
+ codingPath: [any CodingKey],
+ userInfo: [CodingUserInfoKey: Any],
_: T.Type = T.self,
from cell: PostgresCell,
in context: PostgresDecodingContext,
- file: String, line: Int
+ file: String,
+ line: Int
) throws -> T {
- /// Preferred modern fast-path: Direct conformance to ``PostgresDecodable``, let the cell decode.
+ /// Preferred modern fast-path: Direct conformance to `PostgresDecodable`, let the cell decode.
if let fastPathType = T.self as? any PostgresDecodable.Type {
let cellToDecode: PostgresCell
@@ -127,8 +137,8 @@ struct PostgresDataTranslation {
cellToDecode = cell
}
return try cellToDecode.decode(fastPathType, context: context, file: file, line: line) as! T
-
- /// Legacy "fast"-path: Direct conformance to ``PostgresDataConvertible``; use is deprecated.
+
+ /// Legacy "fast"-path: Direct conformance to `PostgresDataConvertible`; use is deprecated.
} else if let legacyPathType = T.self as? any PostgresLegacyDataConvertible.Type {
let legacyData = PostgresData(type: cell.dataType, typeModifier: nil, formatCode: cell.format, value: cell.bytes)
@@ -139,8 +149,8 @@ struct PostgresDataTranslation {
}
return result as! T
}
-
- /// Slow path: Descend through the ``Decodable`` machinery until we fail or find something we can convert.
+
+ /// Slow path: Descend through the `Decodable` machinery until we fail or find something we can convert.
else {
do {
return try T.init(from: ArrayAwareBoxUwrappingDecoder(
@@ -172,7 +182,7 @@ struct PostgresDataTranslation {
debugDescription: "\(String(reflecting: error))",
underlyingError: error
)
-
+
switch error.code {
case .typeMismatch: throw DecodingError.typeMismatch(T.self, context)
case .missingData: throw DecodingError.valueNotFound(T.self, context)
@@ -181,35 +191,38 @@ struct PostgresDataTranslation {
}
}
}
-
+
static func encode(
value: T,
in context: PostgresEncodingContext,
to bindings: inout PostgresBindings,
- file: String = #fileID, line: Int = #line
+ file: String = #fileID,
+ line: Int = #line
) throws {
- /// Preferred modern fast-path: Direct conformance to ``PostgresEncodable``
+ /// Preferred modern fast-path: Direct conformance to `PostgresEncodable`
if let fastPathValue = value as? any PostgresEncodable {
try bindings.append(fastPathValue, context: context)
}
- /// Legacy "fast"-path: Direct conformance to ``PostgresDataConvertible``; use is deprecated.
+ /// Legacy "fast"-path: Direct conformance to `PostgresDataConvertible`; use is deprecated.
else if let legacyPathValue = value as? any PostgresDataTranslation.PostgresLegacyDataConvertible {
guard let legacyData = legacyPathValue.postgresData else {
throw EncodingError.invalidValue(value, .init(codingPath: [], debugDescription: "Couldn't get PSQL encoding from value '\(value)'"))
}
bindings.append(legacyData)
}
- /// Slow path: Descend through the ``Encodable`` machinery until we fail or find something we can convert.
+ /// Slow path: Descend through the `Encodable` machinery until we fail or find something we can convert.
else {
try bindings.append(self.encode(codingPath: [], userInfo: [:], value: value, in: context, file: file, line: line))
}
}
-
+
internal /*fileprivate*/ static func encode(
- codingPath: [any CodingKey], userInfo: [CodingUserInfoKey: Any],
+ codingPath: [any CodingKey],
+ userInfo: [CodingUserInfoKey: Any],
value: T,
in context: PostgresEncodingContext,
- file: String, line: Int
+ file: String,
+ line: Int
) throws -> PostgresData {
// TODO: Avoid repeating the conformance checks here, or at the very least only repeat them after a second level of nesting...
if let fastPathValue = value as? any PostgresEncodable {
@@ -256,7 +269,7 @@ private final class ArrayAwareBoxUwrappingDecoder= self.data.count
}
-
+
var currentIndex = 0
-
+
mutating func decodeNil() throws -> Bool {
guard self.data[self.currentIndex].value == nil else { return false }
self.currentIndex += 1
return true
}
-
+
mutating func decode(_: T.Type) throws -> T {
// TODO: Don't fake a cell.
let data = self.data[self.currentIndex], cell = PostgresCell(
@@ -297,17 +310,17 @@ private final class ArrayAwareBoxUwrappingDecoder(keyedBy: K.Type) throws -> KeyedDecodingContainer { throw self.rejectNestingError }
mutating func nestedUnkeyedContainer() throws -> any UnkeyedDecodingContainer { throw self.rejectNestingError }
mutating func superDecoder() throws -> any Decoder { throw self.rejectNestingError }
}
-
+
func container(keyedBy: Key.Type) throws -> KeyedDecodingContainer {
throw DecodingError.dataCorrupted(.init(codingPath: self.codingPath, debugDescription: "Dictionary containers must be JSON-encoded"))
}
-
+
func unkeyedContainer() throws -> any UnkeyedDecodingContainer {
// TODO: Find a better way to figure out arrays
guard let array = PostgresData(type: self.cell.dataType, typeModifier: nil, formatCode: self.cell.format, value: self.cell.bytes).array else {
@@ -315,11 +328,11 @@ private final class ArrayAwareBoxUwrappingDecoder any SingleValueDecodingContainer { self }
-
+
func decodeNil() -> Bool { self.cell.bytes == nil }
-
+
func decode(_: T.Type) throws -> T {
try PostgresDataTranslation.decode(
codingPath: self.codingPath + [PostgresKit.SomeCodingKey(stringValue: "(Unwrapping(\(T0.self)))")], userInfo: self.userInfo,
@@ -331,7 +344,7 @@ private final class ArrayAwareBoxUwrappingDecoder: Encoder, SingleValueEncodingContainer {
enum Value {
final class ArrayRef { var contents: [T] = [] }
-
+
case invalid
case indexed(ArrayRef)
case scalar(PostgresData)
@@ -350,7 +363,7 @@ private final class ArrayAwareBoxWrappingPostgresEncoder
case .indexed(_): break // existing array, adopt it for appending (support for superEncoder())
}
}
-
+
var indexedCount: Int {
if case .indexed(let ref) = self { return ref.contents.count }
else { preconditionFailure("Internal error in encoder (requested indexed count from non-indexed state)") }
@@ -361,7 +374,7 @@ private final class ArrayAwareBoxWrappingPostgresEncoder
else { preconditionFailure("Internal error in encoder (attempted store to indexed in non-indexed state)") }
}
}
-
+
var codingPath: [any CodingKey]
let userInfo: [CodingUserInfoKey: Any]
let context: PostgresEncodingContext
@@ -376,22 +389,22 @@ private final class ArrayAwareBoxWrappingPostgresEncoder
self.line = line
self.value = value
}
-
+
func container(keyedBy: K.Type) -> KeyedEncodingContainer {
precondition(!self.value.isValid, "Requested multiple containers from the same encoder.")
return .init(FailureEncoder())
}
-
+
func unkeyedContainer() -> any UnkeyedEncodingContainer {
self.value.requestIndexed()
return ArrayContainer(encoder: self)
}
-
+
func singleValueContainer() -> any SingleValueEncodingContainer {
precondition(!self.value.isValid, "Requested multiple containers from the same encoder.")
return self
}
-
+
struct ArrayContainer: UnkeyedEncodingContainer {
let encoder: ArrayAwareBoxWrappingPostgresEncoder
var codingPath: [any CodingKey] { self.encoder.codingPath }
@@ -420,7 +433,7 @@ private final class ArrayAwareBoxWrappingPostgresEncoder
codingPath: self.codingPath, userInfo: self.userInfo, value: value, in: self.context, file: self.file, line: self.line
))
}
-
+
struct FallbackSentinel: Error {}
/// This is a workaround for the inability of encoders to throw errors in various places. It's still better than fatalError()ing.
diff --git a/Sources/PostgresKit/PostgresDatabase+SQL.swift b/Sources/PostgresKit/PostgresDatabase+SQL.swift
index 95d38c72..5be0b5e5 100644
--- a/Sources/PostgresKit/PostgresDatabase+SQL.swift
+++ b/Sources/PostgresKit/PostgresDatabase+SQL.swift
@@ -7,13 +7,18 @@ extension PostgresDatabase {
public func sql(queryLogLevel: Logger.Level? = .debug) -> some SQLDatabase {
self.sql(encodingContext: .default, decodingContext: .default, queryLogLevel: queryLogLevel)
}
-
+
public func sql(
encodingContext: PostgresEncodingContext,
decodingContext: PostgresDecodingContext,
queryLogLevel: Logger.Level? = .debug
) -> some SQLDatabase {
- PostgresSQLDatabase(database: self, encodingContext: encodingContext, decodingContext: decodingContext, queryLogLevel: queryLogLevel)
+ PostgresSQLDatabase(
+ database: self,
+ encodingContext: encodingContext,
+ decodingContext: decodingContext,
+ queryLogLevel: queryLogLevel
+ )
}
}
@@ -28,22 +33,22 @@ extension PostgresSQLDatabase: SQLDatabase, PostgresDatabase {
var logger: Logger {
self.database.logger
}
-
+
var eventLoop: any EventLoop {
self.database.eventLoop
}
-
+
var version: (any SQLDatabaseReportedVersion)? {
nil // PSQL doesn't send version in wire protocol, must use SQL to read it
}
-
+
var dialect: any SQLDialect {
PostgresDialect()
}
-
- func execute(sql query: any SQLExpression, _ onRow: @escaping @Sendable (any SQLRow) -> ()) -> EventLoopFuture {
+
+ func execute(sql query: any SQLExpression, _ onRow: @escaping @Sendable (any SQLRow) -> Void) -> EventLoopFuture {
let (sql, binds) = self.serialize(query)
-
+
if let queryLogLevel = self.queryLogLevel {
self.logger.log(level: queryLogLevel, "Executing query", metadata: ["sql": .string(sql), "binds": .array(binds.map { .string("\($0)") })])
}
@@ -61,13 +66,13 @@ extension PostgresSQLDatabase: SQLDatabase, PostgresDatabase {
)
} }.map { _ in }
}
-
+
func execute(
sql query: any SQLExpression,
- _ onRow: @escaping @Sendable (any SQLRow) -> ()
+ _ onRow: @escaping @Sendable (any SQLRow) -> Void
) async throws {
let (sql, binds) = self.serialize(query)
-
+
if let queryLogLevel = self.queryLogLevel {
self.logger.log(level: queryLogLevel, "Executing query", metadata: ["sql": .string(sql), "binds": .array(binds.map { .string("\($0)") })])
}
@@ -85,16 +90,15 @@ extension PostgresSQLDatabase: SQLDatabase, PostgresDatabase {
)
}.get()
}
-
-
+
func send(_ request: any PostgresRequest, logger: Logger) -> EventLoopFuture {
self.database.send(request, logger: logger)
}
-
+
func withConnection(_ closure: @escaping (PostgresConnection) -> EventLoopFuture) -> EventLoopFuture {
self.database.withConnection(closure)
}
-
+
func withSession(_ closure: @escaping @Sendable (any SQLDatabase) async throws -> R) async throws -> R {
try await self.withConnection { c in
c.eventLoop.makeFutureWithTask {
diff --git a/Sources/PostgresKit/PostgresDialect.swift b/Sources/PostgresKit/PostgresDialect.swift
index 8502ed6f..43a82ca1 100644
--- a/Sources/PostgresKit/PostgresDialect.swift
+++ b/Sources/PostgresKit/PostgresDialect.swift
@@ -2,7 +2,7 @@ import SQLKit
public struct PostgresDialect: SQLDialect {
public init() {}
-
+
public var name: String {
"postgresql"
}
@@ -70,7 +70,7 @@ public struct PostgresDialect: SQLDialect {
}
public func customDataType(for dataType: SQLDataType) -> (any SQLExpression)? {
- if case let .custom(expr) = dataType, (expr as? SQLRaw)?.sql == "TIMESTAMP" {
+ if case .custom(let expr) = dataType, (expr as? SQLRaw)?.sql == "TIMESTAMP" {
return SQLRaw("TIMESTAMPTZ")
} else if case .blob = dataType {
return SQLRaw("BYTEA")
@@ -85,11 +85,11 @@ public struct PostgresDialect: SQLDialect {
public var unionFeatures: SQLUnionFeatures {
[
- .union, .unionAll,
+ .union, .unionAll,
.intersect, .intersectAll,
- .except, .exceptAll,
+ .except, .exceptAll,
.explicitDistinct,
- .parenthesizedSubqueries
+ .parenthesizedSubqueries,
]
}
@@ -103,7 +103,7 @@ public struct PostgresDialect: SQLDialect {
public func nestedSubpathExpression(in column: any SQLExpression, for path: [String]) -> (any SQLExpression)? {
guard !path.isEmpty else { return nil }
-
+
let descender = SQLList(
[column] + path.dropLast().map(SQLLiteral.string(_:)),
separator: SQLRaw("->")
@@ -112,7 +112,7 @@ public struct PostgresDialect: SQLDialect {
[descender, SQLLiteral.string(path.last!)],
separator: SQLRaw("->>")
)
-
+
return SQLGroupExpression(accessor)
}
}
diff --git a/Sources/PostgresKit/SQLPostgresConfiguration.swift b/Sources/PostgresKit/SQLPostgresConfiguration.swift
index e9d9def5..f143dd74 100644
--- a/Sources/PostgresKit/SQLPostgresConfiguration.swift
+++ b/Sources/PostgresKit/SQLPostgresConfiguration.swift
@@ -76,7 +76,7 @@ public struct SQLPostgresConfiguration: Sendable {
/// > additional information and recommendations.
///
/// [tlsconfig]:
- /// https://swiftpackageindex.com/apple/swift-nio-ssl/main/documentation/niossl/tlsconfiguration
+ /// https://swiftpackageindex.com/apple/swift-nio-ssl/documentation/niossl/tlsconfiguration
public init(url: URL) throws {
guard let comp = URLComponents(url: url, resolvingAgainstBaseURL: true), let username = comp.user else {
throw URLError(.badURL, userInfo: [NSURLErrorFailingURLErrorKey: url, NSURLErrorFailingURLStringErrorKey: url.absoluteString])
@@ -141,7 +141,7 @@ public struct SQLPostgresConfiguration: Sendable {
}
/// Create a ``SQLPostgresConfiguration`` for establishing a connection to a server over a
- /// preestablished `NIOCore/Channel`.
+ /// preestablished `NIOCore.Channel`.
///
/// This is provided for calling code which wants to manage the underlying connection transport on its
/// own, such as when tunneling a connection through SSH.