• v0.9.1 9d8762458c

    Rene Nochebuena released this 2026-03-20 14:16:15 -06:00 | 0 commits to main since this release

    v0.9.1

    code.nochebuena.dev/go/mysql

    Overview

    Patch release that resolves the parseTime, loc, and charset hardcoded-DSN limitation
    documented in v0.9.0. Three new optional Config fields expose those parameters as
    first-class configuration. All interfaces are unchanged; existing code requires no
    modifications.

    What's New

    Configurable DSN parameters

    Config gains three new optional string fields:

    Field Env var Default Description
    Charset MYSQL_CHARSET "utf8mb4" Connection character set (SET NAMES <charset>)
    Loc MYSQL_LOC "UTC" IANA timezone for time.TimeDATETIME conversion
    ParseTime MYSQL_PARSE_TIME "true" Driver-level DATE/DATETIMEtime.Time mapping ("true" / "false")

    Empty fields fall back to their defaults, so existing Config literals produce identical DSN
    output to v0.9.0 — no migration required.

    Example

    // Minimal — defaults produce charset=utf8mb4&loc=UTC&parseTime=true (same as v0.9.0)
    db := mysql.New(logger, mysql.Config{
        Host: "localhost", Port: 3306,
        User: "app", Password: "secret", Name: "mydb",
    })
    
    // Explicit — override when needed
    db := mysql.New(logger, mysql.Config{
        Host: "localhost", Port: 3306,
        User: "app", Password: "secret", Name: "mydb",
        Charset:   "utf8mb4",
        Loc:       "America/Mexico_City",
        ParseTime: "false", // receive raw []byte for DATE columns
    })
    

    Known Limitations & Edge Cases

    • No context on Tx.Commit() / Tx.Rollback(). database/sql does not support it.
    • No query builder. Repository code writes raw SQL strings.
    • No migration helper. Schema migrations are out of scope.
    • Collation cannot be set via DSN. go-sql-driver v1.8.x negotiates the connection
      collation using a 1-byte handshake ID (max 255). MariaDB 11.4+ collations such as
      utf8mb4_uca1400_as_cs carry IDs > 255 and will cause a connection error if passed as a
      DSN collation parameter. Set the desired collation in your schema migrations at the
      database/table level — column-level collation governs ORDER BY, WHERE comparisons,
      and UNIQUE key behaviour regardless of the connection collation.

    Installation

    go get code.nochebuena.dev/go/mysql@v0.9.1
    

    Requires Go 1.21 or later.

    v0.9.x → v1.0.0 Roadmap

    • Consider adding a BeginTx(ctx, opts) variant for explicit transaction isolation levels.
    • Gather production feedback on connection pool defaults before hardening the config.
    • Assess whether *sql.DB stats (OpenConnections, WaitCount) should be surfaced for
      observability.

    v0.9.0

    code.nochebuena.dev/go/mysql

    Overview

    mysql is a database/sql-backed MySQL client that integrates with the launcher lifecycle
    and health check systems. It manages a *sql.DB connection pool, maps MySQL driver errors to
    portable xerrors codes, and provides a UnitOfWork implementation that injects the active
    transaction into the context so repositories participate in transactions without explicit wiring.

    This is the first stable release. The API was designed through multiple architecture reviews
    and validated end-to-end via the todo-api proof-of-concept. It is versioned at v0.9.0 rather
    than v1.0.0 because the library has not yet been exercised in production across all edge cases;
    the pre-1.0 version preserves the option for minor API refinements without a major bump.

    What's Included

    • Executor interface: ExecContext, QueryContext, QueryRowContext using database/sql
      native types (sql.Result, *sql.Rows, *sql.Row)
    • Tx interface: extends Executor with Commit() / Rollback() — no context arguments,
      matching the honest database/sql contract
    • Client interface: GetExecutor(ctx), Begin(ctx), Ping(ctx), HandleError(err)
    • Component interface: composes launcher.Component, health.Checkable, and Client
    • New(logger, cfg) Component constructor; pool opened in OnInit, not at construction
    • Config struct with env-tag support (MYSQL_HOST, MYSQL_PORT, MYSQL_USER,
      MYSQL_PASSWORD, MYSQL_NAME, MYSQL_MAX_CONNS, MYSQL_MIN_CONNS, connection lifetime fields)
    • UnitOfWork interface and NewUnitOfWork(logger, client) constructor using context injection
    • HandleError(err) error package-level function mapping MySQL errors to xerrors:
      • Error 1062 (ER_DUP_ENTRY) → ErrAlreadyExists
      • Errors 1216, 1217, 1451, 1452 (foreign key violations) → ErrInvalidInput
      • sql.ErrNoRowsErrNotFound
      • all other errors → ErrInternal
    • Driver imported as mysqldrv alias in errors.go to avoid name collision with this package
    • health.Checkable implementation at health.LevelCritical

    Installation

    go get code.nochebuena.dev/go/mysql@v0.9.0
    

    Requires Go 1.21 or later. Depends on code.nochebuena.dev/go/health,
    code.nochebuena.dev/go/launcher, code.nochebuena.dev/go/logz,
    code.nochebuena.dev/go/xerrors, and github.com/go-sql-driver/mysql.

    Design Highlights

    database/sql native types throughout. Executor uses sql.Result, *sql.Rows, and
    *sql.Row. Method names follow database/sql convention (ExecContext, QueryContext,
    QueryRowContext). This is intentionally distinct from the postgres module, which uses pgx
    types.

    Honest Tx contract. database/sql does not support per-call context on Commit or
    Rollback. The Tx interface reflects this honestly — Commit() and Rollback() take no
    context — rather than accepting a context argument and silently ignoring it.

    Driver alias prevents name collision. The go-sql-driver/mysql package name would
    collide with this package's name mysql. In errors.go it is imported as mysqldrv; in
    mysql.go it is imported with _ for side-effect driver registration only.

    UnitOfWork via context injection. Same pattern as the postgres module: the active
    *sql.Tx is stored under an unexported ctxTxKey{} in the context. GetExecutor(ctx) returns
    the transaction if present, otherwise *sql.DB.

    Error mapping by error number. HandleError type-asserts to *mysqldrv.MySQLError and
    switches on .Number — not message strings — to map driver errors to xerrors codes.

    Known Limitations & Edge Cases

    • No context on Tx.Commit() / Tx.Rollback(). database/sql does not support it. A
      commit or rollback cannot be cancelled by a deadline or context cancellation.
    • No query builder. Repository code writes raw SQL strings.
    • No migration helper. Schema migrations are out of scope.
    • parseTime=true and loc=UTC are hardcoded in the DSN. Applications that need a
      different timezone or raw time bytes must construct their own DSN.

    v0.9.0 → v1.0.0 Roadmap

    • Evaluate whether DSN parameters (parseTime, loc) should be exposed in Config.
    • Consider adding a BeginTx(ctx, opts) variant for explicit transaction isolation levels.
    • Gather production feedback on connection pool defaults before hardening the config.
    • Assess whether *sql.DB stats (OpenConnections, WaitCount) should be surfaced for observability.
    Downloads