# 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.