# AGENTS.md
## OVERVIEW
Rust library crate implementing PostgreSQL wire protocol v3 with SCRAM-SHA-256/MD5/cleartext auth. Synchronous API, connection pooling via global static `DB_POOL`. Error type: `PgsqlError` enum (not `String`).
## STRUCTURE
```
br-pgsql/
├── src/
│ ├── lib.rs # API surface: Pgsql struct, connect(), pools()
│ ├── config.rs # Config struct, defaults, URL assembly, hostport validation
│ ├── connect.rs # TCP lifecycle, startup/auth, query/execute, read() with retry
│ ├── packet.rs # Wire protocol pack/unpack (2411 lines, largest)
│ ├── pools.rs # Pools, ConnectionGuard, lazy pool, retry logic
│ ├── format.rs # OID→JsonValue type conversion, PG array parsing
│ └── error.rs # PgsqlError enum: Protocol, Connection, Io, Timeout, Pool, Auth
├── tests/index.rs # Integration test (requires live PostgreSQL)
├── examples/ # index.rs uses tokio-postgres (NOT this crate!)
├── Cargo.toml
└── INSTALL.md # Clippy/publish commands
```
## WHERE TO LOOK
| Add PostgreSQL message type | `src/packet.rs` → `MessageType::form()` | Match on byte tag |
| Add field type conversion | `src/format.rs` → `FieldFormat::from_u16()` | Match (code, type_oid) |
| Change auth flow | `src/connect.rs` → `sasl_initial_response_message()` | SCRAM logic |
| Modify pool behavior | `src/pools.rs` → `get_connect()`, `release_conn()` | Global `DB_POOL` |
| Change defaults | `src/config.rs` → `Config::new()` | localhost:5432, postgres/111111 |
| Add error variant | `src/error.rs` → `PgsqlError` enum | Implement Display + From |
| Modify read retry logic | `src/connect.rs` → `read()` | MAX_RETRIES, deadline, MAX_MESSAGE_SIZE |
## COMMANDS
```bash
# Build
cargo build
cargo build --release
# Format
cargo fmt
cargo fmt --check
# Lint
cargo clippy --all-targets --all-features -- -D warnings
cargo clippy --all-targets --all-features -- -D warnings -W clippy::pedantic
# Test (unit — no PostgreSQL needed)
cargo test --lib
# Test (full — requires PostgreSQL at localhost:5432)
cargo test
# Coverage
cargo llvm-cov --lib
cargo llvm-cov --lib --show-missing-lines
# Publish
cargo package --list
cargo publish --dry-run
```
## MODULE BOUNDARIES
| `packet.rs` | Wire protocol only | Network I/O |
| `connect.rs` | Socket lifecycle, read/write | Pool policy |
| `pools.rs` | Pool management, retry | Protocol details |
| `format.rs` | Type conversion | Packet parsing |
| `error.rs` | Error types | Business logic |
| `config.rs` | Configuration | Connection logic |
## CONVENTIONS
- **Error type**: `PgsqlError` enum with variants: Protocol, Connection, Io, Timeout, Pool, Auth
- **Imports**: `crate::...` first, then external, then `std`
- **Logging**: `log` macros only, no `println!` in library code (except `_cleanup_idle_connections`)
- **Numeric widths**: Explicit (`u16`, `u32`, `i32`) for protocol fields
- **JSON**: `json::JsonValue` for config and result rows
- **Tests**: Inline `#[cfg(test)] mod tests` in each source file, mock TCP servers via `TcpListener::bind("127.0.0.1:0")`
- **Test constants**: `#[cfg(test)]` overrides for timeouts/sizes (MAX_RETRIES=3, MAX_MESSAGE_SIZE=128, deadline=200ms, RETRY_SLEEP=1ms)
## ANTI-PATTERNS (THIS PROJECT)
- **DO NOT** expand `unsafe { String::from_utf8_unchecked }` usage — existing tech debt
- **DO NOT** add `.unwrap()` in new parse paths — use fallible conversions
- **DO NOT** introduce async in core crate — sync only, async in examples
- **DO NOT** hold mutex during socket operations — keep lock times short
- **DO NOT** change public API signatures without explicit request
- **DO NOT** suppress type errors with `as any`, `@ts-ignore` equivalents
## KNOWN SHARP EDGES
- `packet.rs` uses `unsafe` UTF-8 conversions extensively — narrow edits only
- `tests/index.rs` requires live PostgreSQL with default creds
- `examples/index.rs` demos `tokio-postgres`, not this crate (misleading)
- Global `DB_POOL` static — all `Pools` instances share same backing store
- Chinese comments throughout — preserve or translate consistently
- `pools.rs` tests use single `pools_all_paths` test to avoid global state races
- `connect.rs` `read()` has `#[cfg(test)]` reduced constants — production values differ
## SECURITY HARDENING (APPLIED)
| Message length validation | `packet.rs:222` | `raw_len < 4` guard |
| Bounds checks on all message types | `packet.rs` | ErrorResponse, Notice, ReadyForQuery, ParameterDescription, DataRow, RowDescription |
| SCRAM nonce prefix validation | `packet.rs:269` | `starts_with(&self.client_nonce)` |
| MAX_MESSAGE_SIZE (256MB) | `connect.rs:129` | Prevents OOM DoS |
| Read deadline (300s) | `connect.rs:133` | Prevents infinite hang |
| Frame completeness check | `connect.rs:170` | `msg.len() > len as usize` |
| Pool retry limit | `pools.rs:87` | 20 attempts max |
| Hostport range validation | `config.rs` | 1-65535, fallback to 5432 |
| Residual payload drain | `packet.rs` | ParseCompletion etc. drain when `len > 0` |
## DEPENDENCIES
| `json` | Config parsing, result rows |
| `hmac` + `sha2` + `base64` + `rand` | SCRAM-SHA-256 auth |
| `md5` | MD5 auth |
| `log` | Diagnostics |
| `tokio-postgres` (dev) | Reference in examples only |
| `env_logger` (dev) | Test logging |
## TEST COVERAGE
**98.89%** line coverage (154 tests, 1.4s). Remaining 38 uncovered lines are test-only panic branches and helper edge cases.
## QUICK CHECKLIST
Before coding:
1. Identify target module boundary
2. Choose smallest validating test command (`cargo test --lib`)
3. Check if change affects DB connection requirements
Before finishing:
1. `cargo fmt --check`
2. `cargo clippy --all-targets --all-features -- -D warnings`
3. `cargo test --lib` (fast, no DB needed)
4. Verify diff is focused and intentional