# ADR-001: database/sql Native Types **Status:** Accepted **Date:** 2026-03-18 ## Context Go's standard `database/sql` package was designed as a database-agnostic abstraction. Using it for MySQL is the conventional approach: the `go-sql-driver/mysql` package registers itself as a `database/sql` driver, and all query operations are expressed through `*sql.DB`, `*sql.Tx`, `*sql.Rows`, `*sql.Row`, and `sql.Result`. An alternative would be to use a MySQL-specific client library analogous to pgx for PostgreSQL. However, there is no dominant MySQL-specific client with the same level of ecosystem adoption and capability that pgx has for PostgreSQL. Using `database/sql` with a standard driver is the practical and broadly understood choice. The `postgres` module deliberately chose pgx native types. MySQL is the counterpart — it uses `database/sql` native types. The two modules are not interchangeable, which is by design. ## Decision The `mysql` module's `Executor` interface uses `database/sql` types throughout: ```go type Executor interface { ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error) QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row } ``` Method names follow the `database/sql` convention (`ExecContext`, `QueryContext`, `QueryRowContext`) rather than the pgx convention (`Exec`, `Query`, `QueryRow`). This matches the underlying `*sql.DB` and `*sql.Tx` method signatures exactly, meaning `*sql.DB` satisfies `Executor` structurally without any wrapping. The `mysqlComponent` embeds `*sql.DB` and delegates directly. `mysqlTx` wraps `*sql.Tx` and delegates to it. ## Consequences - **Positive**: Familiarity — `database/sql` is Go's standard library API, known to most Go developers. - **Positive**: `*sql.DB` and `*sql.Tx` natively satisfy the `Executor` interface, reducing the need for wrapper code. - **Positive**: Any `database/sql`-compatible driver (MariaDB, TiDB, etc.) could be substituted by changing the driver registration without touching the interface. - **Negative**: `sql.Result` is less informative than `pgconn.CommandTag` (no command string, only `LastInsertId` and `RowsAffected`). Applications that need PostgreSQL-style command metadata must use the `postgres` module. - **Negative**: `*sql.Rows` must be closed after use (`defer rows.Close()`). Forgetting this leaks connections back to the pool. - **Note**: The method naming difference from `postgres` (`ExecContext` vs `Exec`) is intentional and honest — it matches the actual API of the underlying library rather than artificially unifying the two modules.