Skip to content

Add a MySQL provider - #8

Merged
bashhack merged 1 commit into
mainfrom
mysql-provider
Oct 22, 2026
Merged

bashhack merged 1 commit into
mainfrom
mysql-provider

Conversation

@bashhack

Copy link
Copy Markdown
Owner

Step 4, PR 1 of 2: the mysql package. PR 2 adds templates (logical clone plus ready databases); until then MySQL is deliberately not listed in the README, per PROVIDER_PARITY.md: isolation without the speed is half the thesis.

db := mysql.Setup(t, testdb.WithMigrator(goose.Dir("./migrations")))   // *sql.DB on a database nobody else touches
db := mysql.New(t, &SqlxInitializer{}, testdb.WithMigrator(mysql.SQLFiles("./schema")))

MySQL's hazard, handled

DROP DATABASE in MySQL does not fail while sessions are connected; it waits on the metadata locks they hold, by default for a year. The provider kills the database's sessions first and runs its admin session with lock_wait_timeout = 5, so a session it could not kill makes cleanup fail loudly rather than hang a CI job. TestCleanupWithOpenTransactionDoesNotHang leaks a transaction holding a lock and checks the drop still completes promptly and the database is gone.

DSNs

The admin DSN may be go-sql-driver's native form (user:pass@tcp(host:port)/db?params) or a mysql:// URL. TEST_MYSQL_URL wins over TEST_DATABASE_URL/DATABASE_URL; a project whose shared variable points at Postgres gets an error that says to set it (which the local .env here now does). Test DSNs are built from the parsed admin config, so parseTime, TLS, timeouts etc. all carry over. mysql.SQLFiles / LoadSQLFile run multi-statement files through a multiStatements connection.

Core: shared plumbing for parity

  • testdb.RunConfiguredMigrations and testdb.RegisterCleanup are what both providers' Setup/New are built from, so they behave identically around a test. One behaviour change: a failed migration under KeepOnFailure now keeps the database (a half-applied migration is what you want to look at).
  • testdb.InspectCommander: a provider says how to open a kept database in its own client. The kept-database log shows psql "…" for Postgres and mysql --host=… --user=… … for MySQL.

Adapters, infrastructure

  • goose infers the MySQL dialect from a native DSN; golang-migrate gains MySQL through its database/mysql driver (native DSN prefixed with mysql://, the form it parses). Tern stays Postgres-only. Both adapters have MySQL tests.
  • mysql:8.4 joins docker-compose.test.yml with durability off (the MySQL equivalent of the Postgres service's fsync=off; ~40% faster DDL and drops, measured) and the CI services. make test/db/start waits for both.
  • MySQL-backed tests skip when no server answers so a Postgres-only checkout stays green, but fail in CI (CI set) so a broken service can never pass silently.

github.com/bashhack/testdb/mysql gives each test its own MySQL database:
mysql.Setup returns a *sql.DB, mysql.New takes a custom initializer, and
migrators, keep-on-failure and parallel use work as they do for
PostgreSQL. Templates are not available yet (MySQL cannot clone a
database); WithTemplate is refused with ErrTemplatesNotSupported, and
the README does not list MySQL until they are.

Cleanup is designed around MySQL's hazard: DROP DATABASE does not fail
while sessions are connected, it waits on their metadata locks, by
default for a year. The provider kills the database's sessions first
and runs with a five-second lock_wait_timeout so a session it could not
kill makes cleanup fail loudly rather than hang. A test leaks an open
transaction and checks the drop still completes promptly.

The admin DSN may be go-sql-driver's native form or a mysql:// URL;
TEST_MYSQL_URL wins over the shared variables, and a PostgreSQL URL in
TEST_DATABASE_URL gets an error that says to set it. Test DSNs are built
from the admin config so every parameter carries over. mysql.SQLFiles
and LoadSQLFile run multi-statement files.

The pieces Setup and New are built from move into core as
testdb.RunConfiguredMigrations and testdb.RegisterCleanup, so both
providers behave identically around a test; a failed migration under
KeepOnFailure now keeps the database too. testdb.InspectCommander lets
a provider say how to open a kept database in its own client, so the
log shows a mysql command line for MySQL and psql for PostgreSQL.

The goose adapter infers the MySQL dialect from a native DSN and the
golang-migrate adapter gains MySQL through its mysql driver. A mysql:8.4
service joins the compose file (durability off, as for Postgres) and
CI; the Makefile waits for both.
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 72.64151% with 58 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
mysql/mysql.go 78.26% 30 Missing ⚠️
helpers.go 0.00% 20 Missing ⚠️
mysql/sqlfiles.go 82.85% 6 Missing ⚠️
migrate/golangmigrate/golangmigrate.go 75.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@bashhack
bashhack merged commit 094eec4 into main Oct 22, 2026
1 check passed
@bashhack
bashhack deleted the mysql-provider branch October 22, 2026 05:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants