Thank you for investing your time trying to improve this plugin. We have some contribution guidelines that you should follow to ensure that your contribution is at its best.
- Go - the version from
go.mod. Any recent Go installation works, the toolchain is downloaded automatically if yours is older. - Python >= 3.12 and uv - all Python tooling runs through uv.
- sqlc on your PATH, in the
version CI pins (
sqlc-versionin.github/workflows/ci.yml). The sqlc version is stamped into every generated file header, so a different version diffs all fixtures. The easiest way to get exactly that version without a Go toolchain is sqlc-bin, whose package version tracks the sqlc version:uv tool install "sqlc-bin==1.31.1". - Docker (or a local PostgreSQL plus a local MySQL) - only needed for the runtime tests.
One-time setup for the Python tooling:
uv sync --group devThe plugin itself is Go code under internal/, compiled to a WASM binary that sqlc generate
executes. The Python code it generates is committed on purpose: test/driver_<driver>/ contains
a sqlc.yaml that generates a matrix of model types into committed subdirectories. CI regenerates
them and fails if the output differs, type-checks them with pyright (strict), lints them with ruff
and runs runtime test suites against real databases. Never edit generated files by hand - change
the generator and regenerate.
After any Go change the plugin must be rebuilt, otherwise sqlc generate keeps using the old
binary:
./scripts/build/build.sh # Linux/macOS
.\scripts\build\build.bat # WindowsThe script builds with GOOS=wasip1 GOARCH=wasm, computes the SHA-256 of the new binary, patches
the sha256: field in the root sqlc.yaml and every test/driver_*/sqlc.yaml (sqlc refuses to
run a plugin whose hash does not match), and copies the .wasm into each test driver directory.
Commit the updated binaries and yaml files together with your Go change.
make tests # go test -shuffle=on ./...
make fmt # golangci-lint fmt
make lint # golangci-lint run
make pipelines # lint-fix + fmt + lint (default goal)make lint passes with zero issues on a clean checkout; please keep it that way. CI runs
the golangci-lint version pinned in .github/workflows/ci.yml; use the same one locally
so make lint matches the CI result.
The pipelines are built with nox. uv run nox runs the default sessions (regeneration,
pyright, ruff, ruff_format and pytest - everything except the _check variants), single
sessions run with uv run nox -s name1 name2:
| Session | What it does |
|---|---|
asyncpg, psycopg_async, psycopg_sync, sqlite3, aiosqlite, pymysql, asyncmy, turso_sync, turso_async |
Regenerate the driver's test fixtures via sqlc, then pyright + ruff |
the _check variants of the driver sessions |
sqlc diff variant: verify the committed generated code is up to date (CI uses these) |
pyright |
Type-check the repository |
ruff_check |
Non-mutating format + lint check (the CI gate) |
ruff, ruff_format |
Format and auto-fix the repository - these sessions rewrite files |
pytest |
Runtime tests against real databases |
The pytest session needs BOTH a local PostgreSQL and a local MySQL - the
session-end cleanup connects to each unconditionally. The connection URI is read from the
POSTGRES_URI environment variable and defaults to
postgresql://root:187187@localhost:5432/root; set the variable only if your instance
differs from that. To start a matching instance with docker, run
docker run --rm --name sqlc-gen-better-python-postgres \
-e POSTGRES_USER=root \
-e POSTGRES_PASSWORD=187187 \
-e POSTGRES_DB=root \
-p 5432:5432 \
-d postgresIt also needs a local MySQL for the pymysql and asyncmy suites, read from
MYSQL_URI with the default mysql://root:187187@localhost:3306/root:
docker run --rm --name sqlc-gen-better-python-mysql \
-e MYSQL_ROOT_PASSWORD=187187 \
-e MYSQL_DATABASE=root \
-p 3306:3306 \
-d mysql:9Stop the containers (after running the tests) with the commands below; --rm removes a
container on stop, so the docker run commands above can be reused as is next time.
docker stop sqlc-gen-better-python-postgres
docker stop sqlc-gen-better-python-mysqlExtra pytest arguments pass through after --, e.g.
uv run nox -s pytest -- test/driver_asyncpg/msgspec/test_msgspec_classes.py -k test_name.
- Change the Go code and run
make tests/make lint. - Rebuild the WASM plugin (see above).
uv run nox- regenerates the fixtures and runs every check on them. The default sessions includepytest, so have the PostgreSQL and MySQL from the section above running. The_checksessions are not needed locally: they verify committed fixtures against a fresh regeneration, which is what CI does with the files you commit.- If your change affects generated output, add coverage: a query/schema case in the test matrix that pins the new behavior, plus a runtime test where it makes sense. CI gates pull requests on patch coverage, so aim for covering every branch of code your PR adds.
- Commit the regenerated fixtures, wasm binaries and yaml files together with the Go change.
Two gotchas worth knowing: generated output must be byte-identical across runs AND a no-op under
ruff format, and .sql files must stay ASCII-only - multi-byte characters in comments corrupt
sqlc's byte-offset parameter rewriting and can silently drop ? placeholders.
We use changie to manage changelog creation, and every PR needs a changelog fragment. changie is set up as a Go tool, so no separate installation is needed:
make changelog # or: go tool changie newChangie will ask you for the following fields:
- Kind: The kind of changes, should be self-explanatory
- Body: A short description of the changes.
- PR: The number of the pull request associated to the changes.
- Github Name: The username of the github account that made the changes. This is used for giving credits to contributors in the changelog. When a fix was diagnosed by someone else (e.g. an issue reporter), feel free to credit them here instead.