35 lines
2.5 KiB
Markdown
35 lines
2.5 KiB
Markdown
|
|
# ADR-001: pgx Native Types
|
||
|
|
|
||
|
|
**Status:** Accepted
|
||
|
|
**Date:** 2026-03-18
|
||
|
|
|
||
|
|
## Context
|
||
|
|
|
||
|
|
Go's standard `database/sql` package provides a database-agnostic interface. Using it with PostgreSQL requires a `database/sql`-compatible driver and means working with `sql.Result`, `*sql.Rows`, and `*sql.Row` — types that were designed for the lowest common denominator across all SQL databases.
|
||
|
|
|
||
|
|
`github.com/jackc/pgx/v5` is a PostgreSQL-specific driver and toolkit that exposes its own richer type system: `pgx.Rows`, `pgx.Row`, and `pgconn.CommandTag`. It provides better performance, native support for PostgreSQL-specific types (arrays, hstore, composite types, etc.), and a more accurate representation of PostgreSQL semantics (e.g., `CommandTag` carries `RowsAffected` as well as the SQL command string).
|
||
|
|
|
||
|
|
The tradeoff is that choosing pgx means explicitly not supporting other databases through the same client type.
|
||
|
|
|
||
|
|
## Decision
|
||
|
|
|
||
|
|
The `postgres` module uses pgx native types throughout its public API. The `Executor` interface uses:
|
||
|
|
|
||
|
|
```go
|
||
|
|
Exec(ctx context.Context, sql string, args ...any) (pgconn.CommandTag, error)
|
||
|
|
Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error)
|
||
|
|
QueryRow(ctx context.Context, sql string, args ...any) pgx.Row
|
||
|
|
```
|
||
|
|
|
||
|
|
The connection pool is `*pgxpool.Pool` (from `pgx/v5/pgxpool`). The transaction type wraps `pgx.Tx`. There is no `database/sql` adapter layer.
|
||
|
|
|
||
|
|
Repository code in application layers imports `pgx` types directly when scanning rows or reading `CommandTag`. This is an explicit, honest API: it says "this is PostgreSQL via pgx" rather than pretending to be database-agnostic.
|
||
|
|
|
||
|
|
## Consequences
|
||
|
|
|
||
|
|
- **Positive**: Full access to PostgreSQL-specific capabilities (binary encoding, COPY protocol, listen/notify, array types, etc.) without impedance mismatch.
|
||
|
|
- **Positive**: `pgconn.CommandTag` carries richer information than `sql.Result` (includes the command string, not just rows affected).
|
||
|
|
- **Positive**: `pgx.Rows` and `pgx.Row` support pgx scan helpers and named arguments.
|
||
|
|
- **Negative**: Repository code cannot be trivially swapped to use the `mysql` module or any other `database/sql` driver — it imports pgx types. This is acceptable because the tier system isolates database clients at Tier 3; application logic in higher tiers operates through domain interfaces, not directly on `Executor`.
|
||
|
|
- **Negative**: `pgx.Rows` must be closed after iteration (`defer rows.Close()`). Forgetting this leaks connections. This is the same discipline as `database/sql` but worth noting.
|