br-pgsql 0.1.20

This is an pgsql
Documentation
# AGENTS.md

Rust 库 crate:实现 PostgreSQL wire protocol v3,支持 SCRAM-SHA-256/MD5/明文认证。同步 API,通过全局静态 `DB_POOL` 实现连接池。错误类型:`PgsqlError` 枚举。

## 命令

```bash
# 构建
cargo build

# 格式化(检查 / 修复)
cargo fmt --check
cargo fmt

# Lint(完成前必须通过)
cargo clippy --all-targets --all-features -- -D warnings

# 更严格的 lint(可选)
cargo clippy --all-targets --all-features -- -D warnings -W clippy::pedantic

# 测试 — 仅单元测试(无需 PostgreSQL,约 1.4 秒)
cargo test --lib

# 测试 — 按名称子串运行单个测试
cargo test --lib test_name_substring
# 示例: cargo test --lib hostport_out_of_range

# 测试 — 运行单个模块的测试
cargo test --lib config::tests
cargo test --lib pools::tests

# 测试 — 完整测试套件(需要 PostgreSQL 运行在 localhost:5432,用户名=postgres,密码=111111)
cargo test

# 覆盖率
cargo llvm-cov --lib
cargo llvm-cov --lib --show-missing-lines

# 发布
cargo publish --dry-run
```

## 项目结构

```
src/
├── packet.rs    # 2816 行 — 协议报文打包/解包(最大文件,含 unsafe)
├── connect.rs   # 1190 — TCP 生命周期、启动/认证、查询/执行、参数化查询、带重试的 read()
├── pools.rs     #  512 — 连接池、ConnectionGuard、全局 DB_POOL、重试逻辑
├── format.rs    #  511 — OID→JsonValue 类型转换、PG 数组解析
├── lib.rs       #  284 — 公开 API:Pgsql 结构体、connect()、pools()
├── error.rs     #  199 — PgsqlError 枚举,实现 Display、Error、From
└── config.rs    #   75 — Config 结构体、默认值、URL 拼接、端口校验
tests/index.rs          — 集成测试(需要运行中的 PostgreSQL)
examples/index.rs       — 使用 tokio-postgres,不是本 crate(容易误导)
```

## 改哪里找哪里

| 任务 | 文件 | 入口 |
|------|------|------|
| 添加 PG 消息类型 | `packet.rs` | `MessageType::form()` — 按字节标签匹配 |
| 添加字段类型转换 | `format.rs` | `FieldFormat::from_u16()` — 按 (code, type_oid) 匹配 |
| 修改认证流程 | `connect.rs` | `authenticate()``scram_auth()``md5_auth()` |
| 修改连接池行为 | `pools.rs` | `get_connect()``release_conn()` |
| 修改默认值 | `config.rs` | `Config::new()` — localhost:5432, postgres/111111 |
| 添加错误变体 | `error.rs` | `PgsqlError` 枚举 — 需实现 Display + From |
| 修改读取重试 | `connect.rs` | `read()` — MAX_RETRIES、deadline、MAX_MESSAGE_SIZE |
| 参数化查询 | `connect.rs` + `packet.rs` | `query_params()``execute_params()``pack_query_params()``Query::Bind` |
| 参数化查询(便捷) | `connect.rs` | `query_str()``execute_str()` — 所有参数非 NULL 的简化版 |

## 模块边界(禁止越界)

| 模块 | 职责 | 禁止包含 |
|------|------|----------|
| `packet.rs` | 协议编解码 | 网络 I/O、连接池逻辑 |
| `connect.rs` | Socket 生命周期、读写 | 连接池策略 |
| `pools.rs` | 连接池管理、重试 | 协议细节 |
| `format.rs` | OID→JsonValue 转换 | 报文解析 |
| `error.rs` | 仅错误类型 | 业务逻辑 |
| `config.rs` | 配置解析 | 连接逻辑 |

## 代码风格

### 导入顺序:crate → 外部依赖 → std
```rust
use crate::config::Config;
use crate::error::PgsqlError;
use log::{error, warn};
use std::collections::VecDeque;
use std::sync::{Arc, Mutex};
```

### 错误处理
- 返回 `Result<T, PgsqlError>` — 禁止 `Result<T, String>`
- 映射 I/O 错误:`.map_err(|e| PgsqlError::Connection(e.to_string()))?`
- 新增的解析/协议路径禁止 `.unwrap()` — 使用可失败转换
- Mutex 加锁:`unwrap_or_else(PoisonError::into_inner)`(现有模式)

### 类型
- 协议字段使用显式数值宽度:`u16``u32``i32` — 禁止用 `usize` 表示线上数据
- `json::JsonValue` 用于配置输入和查询结果行
- `BTreeMap<String, String>` 用于有序键值映射(不用 HashMap)
- `Vec<u8>` 用于原始协议缓冲区

### 命名
- 结构体:`PascalCase``Connect``Packet``ConnectionGuard``FieldFormat`
- 枚举变体:`PascalCase``PgsqlError::Protocol(String)``FieldFormat::BigInt`
- 函数:`snake_case``pack_first()``get_connect()``from_u16()`
- 常量:`SCREAMING_SNAKE``MAX_RETRIES``MAX_MESSAGE_SIZE``DB_POOL`

### 日志
- 仅使用 `log` crate 宏:`debug!``warn!``error!`
- 库代码中禁止 `println!`

### 注释
- 代码中已有大量中文注释 — 编辑附近代码时保留原有注释
- 文档注释使用中文:`/// 第一包``/// 调试``/// 账号`
- 如需翻译,整个代码块统一翻译

### 测试
- 每个源文件底部使用 `#[cfg(test)] mod tests` 内联测试
- Mock TCP 服务器:`TcpListener::bind("127.0.0.1:0")` 获取随机端口
- 辅助函数:`pg_msg(tag: u8, payload: &[u8]) -> Vec<u8>` 构建 PG 协议消息
- `#[cfg(test)]` 常量覆盖:MAX_RETRIES=3、MAX_MESSAGE_SIZE=128、deadline=200ms
- `pools.rs` 使用单个 `pools_all_paths` 测试以避免全局 `DB_POOL` 竞态
- 连接池测试中清理 `DB_POOL``DB_POOL.lock().unwrap_or_else(|e| e.into_inner()).clear()`

## 反模式(硬性规则)

- **禁止** 新增 `unsafe { String::from_utf8_unchecked }` — 现有技术债,不要扩大
- **禁止** 新增解析/协议路径中使用 `.unwrap()` — 使用可失败转换
- **禁止** 在核心 crate 中引入 async — 仅同步(async 仅限 examples/)
- **禁止** 持有 mutex 期间进行 socket 操作 — 保持锁范围最小
- **禁止** 未经明确要求修改公开 API 签名
- **禁止** 类型错误抑制(`#[allow(...)]` 隐藏真实问题)

## 已知陷阱

- `packet.rs` 大量使用 `unsafe` UTF-8 转换 — 仅做小范围精确修改
- 全局 `DB_POOL` 静态变量 — 所有 `Pools` 实例共享同一个 `VecDeque<Connect>`
- `connect.rs``read()``#[cfg(test)]` 下使用缩小的常量 — 生产值不同
- `tests/index.rs` 需要运行中的 PostgreSQL 且使用默认凭据(postgres/111111)
- `examples/index.rs` 演示的是 `tokio-postgres`,不是本 crate

## 依赖

| Crate | 用途 |
|-------|------|
| `json` | 配置解析、结果行 |
| `hmac` + `sha2` + `base64` + `rand` | SCRAM-SHA-256 认证 |
| `md5` | MD5 认证 |
| `log` | 日志宏 |

## 检查清单

编码前:
1. 确认改动属于哪个模块边界
2. 选择最小测试命令:`cargo test --lib`(无需数据库)

完成前:
1. `cargo fmt --check`
2. `cargo clippy --all-targets --all-features -- -D warnings`
3. `cargo test --lib`
4. 确认 diff 聚焦 — 无无关改动