mcpField
P
postgres-nio

postgres-nio

github.com/wendylabsinc开发6264 次安装5 周前更新
代码仓库

关于 postgres-nio

postgres-nio 是面向 Swift 开发者的 PostgreSQL 客户端库,适用于异步数据库应用。它结合 async/await、连接池、参数化查询构造和流式行处理。

主要功能

  1. 01支持 async/await 的 PostgreSQL 客户端
  2. 02连接池
  3. 03类型安全的插值查询
  4. 04按位置或列名解码行
  5. 05预处理语句与事务
  6. 06流式结果和 COPY FROM

适用场景

  1. 01构建异步 Swift 数据库服务
  2. 02对 PostgreSQL 执行参数化查询
  3. 03通过背压流式处理大型查询结果
  4. 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、Int64integer、bigint
Float、Doublereal、double precision
Stringtext、varchar
Boolboolean
Datetimestamp、timestamptz
Databytea
UUIDuuid
[T]array
JSON 类型json、jsonb

参考文件

根据需要加载以下文件,了解特定主题:

  • references/postgres-patterns.md - 用于保障 SQL 安全的字符串插值、用于编码/解码的协议层级、用于行解码的可变参数泛型、预处理语句模式、层级状态机、支持背压的行流式处理、COPY FROM 批量加载、线路协议编码

评论

支持 Markdown