Skip to content

Commit 2c13bc8

Browse files
tibGErP83
andauthored
Align codebase with feather database API 1.0.0-beta.3 release (#5)
⚠️ Breaking changes: - With-style API for transaction & connection - Using RowSequence in a handler instead of a return value - Query result renamed to RowSequence - Remove execute API from database client - Improve transaction error interface - Sendable fixes --------- Co-authored-by: GErP83 <gurrka@gmail.com>
1 parent 4d817ff commit 2c13bc8

15 files changed

Lines changed: 1116 additions & 843 deletions

.github/workflows/deployment.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ on:
55
tags:
66
- 'v*'
77
- '[0-9]*'
8+
workflow_dispatch:
89

910
jobs:
1011

.github/workflows/testing.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
format_check_enabled : true
1616
broken_symlink_check_enabled : true
1717
unacceptable_language_check_enabled : true
18-
shell_check_enabled : true
18+
shell_check_enabled : false
1919
docs_check_enabled : false
2020
api_breakage_check_enabled : false
2121
license_header_check_enabled : false
@@ -27,7 +27,7 @@ jobs:
2727
uses: BinaryBirds/github-workflows/.github/workflows/extra_soundness.yml@main
2828
with:
2929
local_swift_dependencies_check_enabled : true
30-
headers_check_enabled : true
30+
headers_check_enabled : false
3131
docc_warnings_check_enabled : true
3232

3333
swiftlang_tests:

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ SHELL=/bin/bash
22

33
baseUrl = https://raw.githubusercontent.com/BinaryBirds/github-workflows/refs/heads/main/scripts
44

5-
check: symlinks language deps lint headers
5+
check: symlinks language deps lint headers docc-warnings package
6+
7+
package:
8+
curl -s $(baseUrl)/check-swift-package.sh | bash
69

710
symlinks:
811
curl -s $(baseUrl)/check-broken-symlinks.sh | bash

Package.resolved

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ let package = Package(
3737
dependencies: [
3838
.package(url: "https://github.com/apple/swift-log", from: "1.6.0"),
3939
.package(url: "https://github.com/vapor/postgres-nio", from: "1.27.0"),
40-
.package(url: "https://github.com/feather-framework/feather-database", exact: "1.0.0-beta.2"),
40+
.package(url: "https://github.com/feather-framework/feather-database", exact: "1.0.0-beta.3"),
4141
// [docc-plugin-placeholder]
4242
],
4343
targets: [

README.md

Lines changed: 33 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,31 @@
22

33
Postgres driver implementation for the abstract [Feather Database](https://github.com/feather-framework/feather-database) Swift API package.
44

5-
[
6-
![Release: 1.0.0-beta.2](https://img.shields.io/badge/Release-1%2E0%2E0--beta%2E2-F05138)
7-
](
8-
https://github.com/feather-framework/feather-postgres-database/releases/tag/1.0.0-beta.2
9-
)
5+
[![Release: 1.0.0-beta.2](https://img.shields.io/badge/Release-1%2E0%2E0--beta%2E2-F05138)](https://github.com/feather-framework/feather-postgres-database/releases/tag/1.0.0-beta.2)
106

117
## Features
128

13-
- 🤝 Postgres driver for Feather Database
14-
- 😱 Automatic query parameter escaping via Swift string interpolation.
15-
- 🔄 Async sequence query results with `Decodable` row support.
16-
- 🧵 Designed for modern Swift concurrency
17-
- 📚 DocC-based API Documentation
18-
- Unit tests and code coverage
9+
- Postgres driver for Feather Database
10+
- Automatic query parameter escaping via Swift string interpolation.
11+
- Async sequence query results with `Decodable` row support.
12+
- Designed for modern Swift concurrency
13+
- DocC-based API Documentation
14+
- Unit tests and code coverage
1915

2016
## Requirements
2117

2218
![Swift 6.1+](https://img.shields.io/badge/Swift-6%2E1%2B-F05138)
2319
![Platforms: Linux, macOS, iOS, tvOS, watchOS, visionOS](https://img.shields.io/badge/Platforms-Linux_%7C_macOS_%7C_iOS_%7C_tvOS_%7C_watchOS_%7C_visionOS-F05138)
24-
20+
2521
- Swift 6.1+
2622

27-
- Platforms:
28-
- Linux
29-
- macOS 15+
30-
- iOS 18+
31-
- tvOS 18+
32-
- watchOS 11+
33-
- visionOS 2+
23+
- Platforms:
24+
- Linux
25+
- macOS 15+
26+
- iOS 18+
27+
- tvOS 18+
28+
- watchOS 11+
29+
- visionOS 2+
3430

3531
## Installation
3632

@@ -46,19 +42,13 @@ Then add `FeatherPostgresDatabase` to your target dependencies:
4642
.product(name: "FeatherPostgresDatabase", package: "feather-postgres-database"),
4743
```
4844

49-
5045
## Usage
51-
52-
[
53-
![DocC API documentation](https://img.shields.io/badge/DocC-API_documentation-F05138)
54-
](
55-
https://feather-framework.github.io/feather-postgres-database/documentation/featherpostgresdatabase/
56-
)
5746

58-
API documentation is available at the following link.
47+
API documentation is available at the link below:
5948

60-
> [!TIP]
61-
> Avoid calling `database.execute` while in a transaction; use the transaction `connection` instead.
49+
[![DocC API documentation](https://img.shields.io/badge/DocC-API_documentation-F05138)](https://feather-framework.github.io/feather-postgres-database/)
50+
51+
Here is a brief example:
6252

6353
```swift
6454
import Logging
@@ -100,14 +90,16 @@ try await withThrowingTaskGroup(of: Void.self) { group in
10090
}
10191
// execute some query
10292
group.addTask {
103-
let result = try await database.execute(
104-
query: #"""
105-
SELECT
106-
version() AS "version"
107-
WHERE
108-
1=\#(1);
109-
"""#
110-
)
93+
let result = try await database.withConnection { connection in
94+
try await connection.run(
95+
query: #"""
96+
SELECT
97+
version() AS "version"
98+
WHERE
99+
1=\#(1);
100+
"""#
101+
)
102+
}
111103

112104
for try await item in result {
113105
let version = try item.decode(column: "version", as: String.self)
@@ -122,7 +114,6 @@ try await withThrowingTaskGroup(of: Void.self) { group in
122114
> [!WARNING]
123115
> This repository is a work in progress, things can break until it reaches v1.0.0.
124116
125-
126117
## Other database drivers
127118

128119
The following database driver implementations are available for use:
@@ -133,12 +124,12 @@ The following database driver implementations are available for use:
133124
## Development
134125

135126
- Build: `swift build`
136-
- Test:
137-
- local: `swift test`
138-
- using Docker: `make docker-test`
127+
- Test:
128+
- local: `swift test`
129+
- using Docker: `make docker-test`
139130
- Format: `make format`
140131
- Check: `make check`
141132

142133
## Contributing
143134

144-
[Pull requests](https://github.com/feather-framework/feather-postgres-database/pulls) are welcome. Please keep changes focused and include tests for new logic. 🙏
135+
[Pull requests](https://github.com/feather-framework/feather-postgres-database/pulls) are welcome. Please keep changes focused and include tests for new logic.

Sources/FeatherPostgresDatabase/PostgresConnection.swift

Lines changed: 0 additions & 37 deletions
This file was deleted.

Sources/FeatherPostgresDatabase/PostgresDatabaseClient.swift

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,13 @@ import FeatherDatabase
99
import Logging
1010
import PostgresNIO
1111

12-
/// Make Postgres transaction errors conform to `DatabaseTransactionError`.
13-
///
14-
/// This allows Postgres errors to flow through `DatabaseError`.
15-
extension PostgresTransactionError: @retroactive DatabaseTransactionError {}
16-
1712
/// A Postgres-backed database client.
1813
///
1914
/// Use this client to execute queries and manage transactions on Postgres.
2015
public struct PostgresDatabaseClient: DatabaseClient {
16+
public typealias Connection = PostgresDatabaseConnection
2117

22-
var client: PostgresClient
18+
var client: PostgresNIO.PostgresClient
2319
var logger: Logger
2420

2521
/// Create a Postgres database client.
@@ -29,7 +25,7 @@ public struct PostgresDatabaseClient: DatabaseClient {
2925
/// - client: The underlying Postgres client.
3026
/// - logger: The logger for database operations.
3127
public init(
32-
client: PostgresClient,
28+
client: PostgresNIO.PostgresClient,
3329
logger: Logger
3430
) {
3531
self.client = client
@@ -41,18 +37,21 @@ public struct PostgresDatabaseClient: DatabaseClient {
4137
/// Execute work using a managed Postgres connection.
4238
///
4339
/// The closure receives a Postgres connection for the duration of the call.
44-
/// - Parameters:
45-
/// - isolation: The actor isolation for the operation.
46-
/// - closure: A closure that receives the connection.
40+
/// - Parameter: closure: A closure that receives the connection.
4741
/// - Throws: A `DatabaseError` if connection handling fails.
4842
/// - Returns: The query result produced by the closure.
4943
@discardableResult
50-
public func connection<T>(
51-
isolation: isolated (any Actor)? = #isolation,
52-
_ closure: (PostgresConnection) async throws -> sending T,
53-
) async throws(DatabaseError) -> sending T {
44+
public func withConnection<T>(
45+
_ closure: (Connection) async throws -> T,
46+
) async throws(DatabaseError) -> T {
5447
do {
55-
return try await client.withConnection(closure)
48+
return try await client.withConnection { connection in
49+
let databaseConnection = PostgresDatabaseConnection(
50+
connection: connection,
51+
logger: logger
52+
)
53+
return try await closure(databaseConnection)
54+
}
5655
}
5756
catch let error as DatabaseError {
5857
throw error
@@ -65,25 +64,30 @@ public struct PostgresDatabaseClient: DatabaseClient {
6564
/// Execute work inside a Postgres transaction.
6665
///
6766
/// The closure is wrapped in a transactional scope.
68-
/// - Parameters:
69-
/// - isolation: The actor isolation for the operation.
70-
/// - closure: A closure that receives the connection.
67+
/// - Parameter: closure: A closure that receives the connection.
7168
/// - Throws: A `DatabaseError` if the transaction fails.
7269
/// - Returns: The query result produced by the closure.
7370
@discardableResult
74-
public func transaction<T>(
75-
isolation: isolated (any Actor)? = #isolation,
76-
_ closure: ((PostgresConnection) async throws -> sending T),
77-
) async throws(DatabaseError) -> sending T {
71+
public func withTransaction<T>(
72+
_ closure: (Connection) async throws -> T,
73+
) async throws(DatabaseError) -> T {
7874
do {
7975
return try await client.withTransaction(
80-
logger: logger,
81-
isolation: isolation,
82-
closure
83-
)
76+
logger: logger
77+
) { connection in
78+
let databaseConnection = PostgresDatabaseConnection(
79+
connection: connection,
80+
logger: logger
81+
)
82+
return try await closure(databaseConnection)
83+
}
8484
}
8585
catch let error as PostgresTransactionError {
86-
throw .transaction(error)
86+
throw .transaction(
87+
PostgresDatabaseTransactionError(
88+
underlyingError: error
89+
)
90+
)
8791
}
8892
catch {
8993
throw .connection(error)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//
2+
// PostgresDatabaseConnection.swift
3+
// feather-postgres-database
4+
//
5+
// Created by Tibor Bödecs on 2026. 01. 10.
6+
//
7+
8+
import FeatherDatabase
9+
import PostgresNIO
10+
11+
public struct PostgresDatabaseConnection: DatabaseConnection {
12+
13+
public typealias Query = PostgresDatabaseQuery
14+
public typealias RowSequence = PostgresDatabaseRowSequence
15+
16+
var connection: PostgresConnection
17+
public var logger: Logger
18+
19+
/// Execute a Postgres query on this connection.
20+
///
21+
/// This wraps `PostgresNIO` query execution and maps errors.
22+
/// - Parameters:
23+
/// - query: The Postgres query to execute.
24+
/// - handler: A closure that receives the RowSequence result.
25+
/// - Throws: A `DatabaseError` if the query fails.
26+
/// - Returns: A query result containing the returned rows.
27+
@discardableResult
28+
public func run<T: Sendable>(
29+
query: Query,
30+
_ handler: (RowSequence) async throws -> T = { $0 }
31+
) async throws(DatabaseError) -> T {
32+
do {
33+
let sequence = try await connection.query(
34+
.init(
35+
unsafeSQL: query.sql,
36+
binds: query.bindings
37+
),
38+
logger: logger
39+
)
40+
41+
return try await handler(
42+
PostgresDatabaseRowSequence(
43+
backingSequence: sequence
44+
)
45+
)
46+
}
47+
catch {
48+
throw .query(error)
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)