# einherjar/db-mysql [![version](https://img.shields.io/badge/version-v1.0.0-5C4EE5?style=flat-square)](https://code.nochebuena.dev/einherjar/db-mysql) [![license](https://img.shields.io/badge/license-AGPL--3.0-22863A?style=flat-square)](LICENSE) [![go](https://img.shields.io/badge/Go-1.26+-00ADD8?style=flat-square&logo=go&logoColor=white)](https://go.dev) [![health](https://img.shields.io/badge/health-critical-D73A49?style=flat-square)]() > A warrior's deeds are committed to stone so that what they built survives the builder. `code.nochebuena.dev/einherjar/db-mysql` is the MySQL/MariaDB database component of the Einherjar framework. It wraps `database/sql` with `go-sql-driver/mysql` behind a lifecycle-aware `Component`, exposes a uniform `Executor` interface for queries and transactions, and provides a `UnitOfWork` that injects the active transaction into context — so repositories never need to know whether they are inside a transaction or not. --- ## Usage ### Setup ```go import dbmysql "code.nochebuena.dev/einherjar/db-mysql" db := dbmysql.New(logger, dbmysql.DefaultConfig()) launcher.Append(db) // OnInit opens pool; OnStop closes it health.Register(db) // PING health check; LevelCritical ``` ### Querying ```go // GetExecutor returns the pool when called outside a UnitOfWork. rows, err := db.GetExecutor(ctx).QueryContext(ctx, "SELECT id, name FROM users WHERE active = ?", true) defer rows.Close() var name string err := db.GetExecutor(ctx).QueryRowContext(ctx, "SELECT name FROM users WHERE id = ?", id).Scan(&name) ``` ### Manual transaction ```go tx, err := db.Begin(ctx) if err != nil { return err } defer tx.Rollback() _, err = tx.ExecContext(ctx, "UPDATE accounts SET balance = balance - ? WHERE id = ?", amount, fromID) if err != nil { return err } return tx.Commit() ``` `BeginTx` is available when you need explicit isolation level control: ```go tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable}) ``` ### Unit of work (recommended) `UnitOfWork` wraps the transaction in context so every call to `GetExecutor` inside `uow.Do` automatically returns the active transaction. Repositories require no changes. ```go uow := dbmysql.NewUnitOfWork(logger, db) err := uow.Do(ctx, func(ctx context.Context) error { _, err := db.GetExecutor(ctx).ExecContext(ctx, "INSERT INTO orders (...) VALUES (...)", ...) return err }) ``` If the function returns an error, the transaction is rolled back. If it returns nil, the transaction is committed. ### Error handling ```go if err := db.HandleError(someErr); err != nil { // MySQL error numbers mapped to xerrors: // 1062 ER_DUP_ENTRY → ErrAlreadyExists // 1216 ER_NO_REFERENCED_ROW → ErrPreconditionFailed // 1048 ER_BAD_NULL_ERROR → ErrInvalidInput // context.Canceled → ErrCancelled // context.DeadlineExceeded → ErrDeadlineExceeded } ``` `HandleError` is also available as a package-level function: `dbmysql.HandleError(err)`. --- ## Environment variables | Variable | Required | Default | Description | |---|---|---|---| | `EINHERJAR_MYSQL_HOST` | Yes | — | MySQL host | | `EINHERJAR_MYSQL_PORT` | No | `3306` | Listen port | | `EINHERJAR_MYSQL_USER` | Yes | — | Database user | | `EINHERJAR_MYSQL_PASSWORD` | Yes | — | Database password | | `EINHERJAR_MYSQL_NAME` | Yes | — | Database name | | `EINHERJAR_MYSQL_MAX_CONNS` | No | `5` | Maximum open connections | | `EINHERJAR_MYSQL_MIN_CONNS` | No | `2` | Minimum idle connections | | `EINHERJAR_MYSQL_MAX_CONN_LIFETIME` | No | `1h` | Maximum connection lifetime | | `EINHERJAR_MYSQL_MAX_CONN_IDLE_TIME` | No | `30m` | Maximum idle time before connection is closed | | `EINHERJAR_MYSQL_CHARSET` | No | `utf8mb4` | Connection charset | | `EINHERJAR_MYSQL_LOC` | No | `UTC` | Timezone location | | `EINHERJAR_MYSQL_PARSE_TIME` | No | `true` | Parse `DATE`/`DATETIME` as `time.Time` | > **Note:** Set collation at the schema level (DDL), not in the DSN. MariaDB 11.4+ collation names exceed the 1-byte handshake limit in `go-sql-driver`. --- ## Dependency graph ``` contracts (zero dependencies) ↑ core ↑ db-mysql (contracts, core, go-sql-driver/mysql) ↑ your app ``` --- ## Verification ```bash cd db-mysql/ go build ./... go vet ./... go test ./... gofmt -l . ``` --- > *The hall is built before the warriors arrive.* > *That is the only guarantee worth making.*