Repository navigation
Contributing
A summary of the repository's CONTRIBUTING.md. The file in the repository is authoritative.
On this page: Repository layout · Running tests · Conventions · Adding a builder method · Adding a dialect
| Path | Contents |
|---|---|
db/ |
The public package: builders, compiler, executor, typed API |
internal/chaingen |
Generator for the builders' chain methods (db/chain_gen.go, run with go generate ./db) |
internal/dialect |
Per-database Grammar implementations (quoting, placeholders, LIKE, dates, locks, index hints) |
internal/rowmap |
Rows to structs, maps and scalars, and structs back to rows |
internal/jsonpath |
col->a->b[0] selectors rendered into JSON path literals |
internal/keyset |
The cursor token codec (decodes untrusted input) |
internal/rawsql |
Argument interpolation for ToRawSQL
|
internal/errs |
Error values shared with db, which re-exports them |
test/benchmark/ |
Separate module: quill vs sqlbuilder, goqu, squirrel, sqlx and GORM |
test/integration/ |
Separate module: end-to-end tests on SQLite, PostgreSQL, MySQL and MariaDB |
docs/ |
The guides |
The root module has no third-party runtime dependencies and should stay that way. testify is used from _test.go files only. Anything that needs a driver or test infrastructure belongs in test/integration/.
Run the same commands CI runs before you push:
go build ./...
go vet ./...
golangci-lint run ./...
go test -race ./... # unit and golden testsIntegration suites (one per database):
cd test/integration
go test -race ./... # SQLite, PostgreSQL, MySQL (and MariaDB)
go test -run TestSQLite ./... # no container needed
go test -run TestPostgres ./... # starts a PostgreSQL container- PostgreSQL, MySQL and MariaDB suites use testcontainers and need Docker. Without a reachable Docker daemon they skip.
-
GOQUERY_SKIP_CONTAINERS=1skips them on purpose.
The Makefile wraps these (make help lists targets: build, lint, test, bench, fuzz, ...).
| Layer | Proves |
|---|---|
Golden SQL (db/*_test.go) |
Exact SQL and arguments for every dialect, no database needed |
Parity (chain_gen.go) |
Query[T] methods always match SelectBuilder methods |
Unit (internal/*) |
Each internal package in isolation |
Examples (db/example_test.go) |
Documentation that is executed and checked |
| Fuzz | Quoting, placeholder and untrusted-input code never misbehave |
End to end (test/integration) |
Real databases accept the SQL and return the right rows |
A failing fuzz input is written under testdata/fuzz/. Fix the cause, then commit that file. It becomes a regression case.
- Every SQL change gets a golden test for all dialects, in the file named after the source file it covers.
- Behaviour that depends on the database also gets an integration test.
-
Errors are sentinels (
db.ErrNoWhere, ...), wrapped with%wand context. Do not panic on bad input. Only theMust...functions panic. - Values are always bound. Never concatenate a value into SQL. Identifiers always go through the quoting helpers.
-
Be explicit about dialect gaps: return
db.ErrUnsupportedrather than emit SQL that silently differs. - Format with
gofmt, keepgolangci-lintclean, and write doc comments on exported names.
- Add the method on the internal
querytype indb/builder.go(or the file for its group). Record bad arguments withb.fail(err), do not panic. - Add the clause or state, and compile it in
db/grammar.go(orwrite.go,join.go,json.go). Write arguments throughc.param/c.valueso numbering stays right. - Add a golden test for every dialect, plus an error test.
- Run
go generate ./db. Never editdb/chain_gen.goby hand. - Add the method to the relevant guide and the method reference.
- If a database could reject or reinterpret the SQL, add an integration case.
- Implement
dialect.Grammarininternal/dialect/<name>.gowith a constructor next to the others. - Add the constant to
Dialectindb/dialect.go, itsString()case, and its branch inDialect.grammar(). - Handle structural differences (update/delete with joins, upsert, truncate,
RETURNING, JSON updates) in theswitchstatements indb/write.goanddb/json.go, or returndb.ErrUnsupported. - Add golden cases to every table-driven test, then a
test/integration/<dialect>_test.gowith its container, schema and suite. - Document it on the Dialect Differences page.
When a database is mostly another one (MariaDB is mostly MySQL), do not copy the grammar. Embed the parent grammar and override only what differs. Probe the real server before copying another project's choices: a grammar written for a different project records what that project chose, not what is required.
Examples in the guides are written to be correct as shown. When you change behaviour, update the guide and keep go doc comments accurate. Examples in db/example_test.go are executed by go test.
quill wiki · v0.2.0 · Repository · pkg.go.dev · Report an issue · MIT License
Basics
Building queries
Data access
- Writing Data
- Reading Data
- Pagination and Chunking
- Transactions
- Typed API
- Struct Mapping
- Hooks and Options
Reference
Guides