关于 postgres-nio
postgres-nio 是面向 Swift 开发者的 PostgreSQL 客户端库,适用于异步数据库应用。它结合 async/await、连接池、参数化查询构造和流式行处理。
主要功能
- 01支持 async/await 的 PostgreSQL 客户端
- 02连接池
- 03类型安全的插值查询
- 04按位置或列名解码行
- 05预处理语句与事务
- 06流式结果和 COPY FROM
适用场景
- 01构建异步 Swift 数据库服务
- 02对 PostgreSQL 执行参数化查询
- 03通过背压流式处理大型查询结果
- 04使用 COPY FROM 批量加载行
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ wendylabsinc/ claude-skills --skill postgres-nio安装后在 Claude Code 中输入/postgres-nio确认已加载。
附带文件references/postgres-patterns.md
详细介绍
PostgreSQL
postgres-nio 库提供 Swift PostgreSQL 客户端,支持完整的 async/await、类型安全的查询构建、连接池和预处理语句。
快速开始
安装
添加到 Package.swift:
dependencies: [
.package(url: "https://github.com/vapor/postgres-nio.git", from: "1.21.0")
]
基本用法
import PostgresNIO
// Using PostgresClient (recommended - includes connection pooling)
let client = PostgresClient(
configuration: .init(
host: "localhost",
port: 5432,
username: "postgres",
password: "secret",
database: "myapp",
tls: .disable
)
)
// Run as a service
try await withThrowingTaskGroup(of: Void.self) { group in
group.addTask { try await client.run() }
// Use client for queries
let rows = try await client.query("SELECT id, name FROM users")
for try await row in rows {
let (id, name) = try row.decode((Int.self, String.self))
print("User \(id): \(name)")
}
group.cancelAll()
}
类型安全的查询
查询使用字符串插值来防止 SQL 注入:
let userId = 123
let email = "[email protected]"
// Interpolated values become parameter bindings ($1, $2, ...)
let query: PostgresQuery = "SELECT * FROM users WHERE id = \(userId) AND email = \(email)"
// Generates: "SELECT * FROM users WHERE id = $1 AND email = $2" with bindings [123, "[email protected]"]
let rows = try await client.query(query)
动态标识符
对表名/列名使用 unescaped(不要用于用户输入!):
let tableName = "users" // Must be trusted, not user input
let query: PostgresQuery = "SELECT * FROM \(unescaped: tableName) WHERE id = \(userId)"
行解码
按位置解码
let rows = try await client.query("SELECT id, name, email, created_at FROM users")
for try await row in rows {
let (id, name, email, createdAt) = try row.decode((Int.self, String.self, String.self, Date.self))
}
按列名解码
for try await row in rows {
let id = try row["id"].decode(Int.self)
let name = try row["name"].decode(String.self)
let email = try row["email"].decode(String?.self) // Optional
}
自定义类型
struct User: Decodable {
let id: Int
let name: String
let email: String
}
// Decode into custom type
for try await row in rows {
let user = try row.decode(User.self)
}
预处理语句
定义可重复使用的预处理语句以提升性能:
struct GetUserByEmail: PostgresPreparedStatement {
typealias Row = (Int, String, Date)
static let sql = "SELECT id, name, created_at FROM users WHERE email = $1"
var email: String
func makeBindings() -> PostgresBindings {
var bindings = PostgresBindings()
bindings.append(email)
return bindings
}
func decodeRow(_ row: PostgresRow) throws -> Row {
try row.decode(Row.self)
}
}
// Execute prepared statement
let results = try await connection.execute(GetUserByEmail(email: "[email protected]"))
for try await (id, name, createdAt) in results {
print("Found user \(id)")
}
事务
try await client.withConnection { connection in
try await connection.query("BEGIN")
do {
try await connection.query("INSERT INTO accounts (id, balance) VALUES (\(fromId), \(fromBalance - amount))")
try await connection.query("INSERT INTO accounts (id, balance) VALUES (\(toId), \(toBalance + amount))")
try await connection.query("COMMIT")
} catch {
try await connection.query("ROLLBACK")
throw error
}
}
批量加载(COPY FROM)
高效加载大型数据集:
let rowCount = try await connection.copyFrom(
table: "users",
columns: ["name", "email", "created_at"],
format: .csv(delimiter: ",", header: false)
) { writer in
for user in users {
var buffer = ByteBuffer()
buffer.writeString("\(user.name),\(user.email),\(user.createdAt)\n")
try await writer.write(buffer)
}
}
print("Inserted \(rowCount) rows")
连接配置
let config = PostgresClient.Configuration(
host: "localhost",
port: 5432,
username: "postgres",
password: "secret",
database: "myapp",
tls: .prefer(.makeClientConfiguration()),
options: .init(
minimumConnections: 2,
maximumConnections: 10,
connectionIdleTimeout: .minutes(5),
keepAliveBehavior: .init(frequency: .seconds(30))
)
)
let client = PostgresClient(configuration: config)
TLS 选项
// Disable TLS (local development)
tls: .disable
// Prefer TLS (use if available)
tls: .prefer(.makeClientConfiguration())
// Require TLS (production)
tls: .require(.makeClientConfiguration())
Unix 域套接字
let config = PostgresClient.Configuration(
unixSocketPath: "/var/run/postgresql/.s.PGSQL.5432",
username: "postgres",
database: "myapp"
)
错误处理
do {
let rows = try await client.query("SELECT * FROM users WHERE id = \(id)")
// ...
} catch let error as PSQLError {
switch error.code {
case .uniqueViolation:
throw MyError.duplicateEntry
case .foreignKeyViolation:
throw MyError.invalidReference
default:
throw error
}
}
流式处理大型结果
结果会自动以流式方式传输,并支持背压:
let rows = try await client.query("SELECT * FROM large_table")
// Process one row at a time - memory efficient
for try await row in rows {
let data = try row.decode(LargeData.self)
try await processData(data)
}
支持的类型
| Swift 类型 | PostgreSQL 类型 |
|---|---|
Int、Int32、Int64 | integer、bigint |
Float、Double | real、double precision |
String | text、varchar |
Bool | boolean |
Date | timestamp、timestamptz |
Data | bytea |
UUID | uuid |
[T] | array |
| JSON 类型 | json、jsonb |
参考文件
根据需要加载以下文件,了解特定主题:
references/postgres-patterns.md- 用于保障 SQL 安全的字符串插值、用于编码/解码的协议层级、用于行解码的可变参数泛型、预处理语句模式、层级状态机、支持背压的行流式处理、COPY FROM 批量加载、线路协议编码
评论
支持 Markdown