diff --git a/README.md b/README.md index 3dd27a150f..fa5ef5e448 100644 --- a/README.md +++ b/README.md @@ -1,536 +1,36 @@ # sqlc: A SQL Compiler -> And lo, the Great One looked down upon the people and proclaimed: -> -> "SQL is actually pretty great" +![go](https://github.com/kyleconroy/sqlc/workflows/go/badge.svg) -sqlc generates **fully type-safe idiomatic Go code** from SQL. Here's how it -works: +sqlc generates **type-safe code** from SQL. Here's how it works: -1. You write SQL queries -1. You run sqlc to generate Go code that presents type-safe interfaces to those - queries -1. You write application code that calls the methods sqlc generated +1. You write queries in SQL. +1. You run sqlc to generate code with type-safe interfaces to those queries. +1. You write application code that calls the generated code. -Seriously, it's that easy. You don't have to write any boilerplate SQL querying -code ever again. +Check out [an interactive example](https://play.sqlc.dev/) to see it in action. -## Preventing Errors +## Overview -But sqlc doesn't just make you more productive by generating boilerplate for -you. sqlc **also prevents entire classes of common errors in SQL code**. Have -you ever: - -- Mixed up the order of the arguments when invoking the query so they didn't - match up with the SQL text -- Updated the name of a column in one query but not another -- Mistyped the name of a column in a query -- Changed the number of arguments in a query but forgot to pass the additional - values -- Changed the type of a column but forgot to change the type in your code? - -All of these errors are *impossible* with sqlc. Wait, what? How? - -sqlc parses all of your queries and the DDL (e.g. `CREATE TABLE`) -statements during the code generation processes so that it knows the names and -types of every column in your tables and every expression in your queries. If -any of them do not match, sqlc *will fail to compile your queries*, preventing -entire classes of runtime problems at compile time. - -Likewise, the methods that sqlc generates for you have a strict arity and -correct Go type definitions that match your columns. So if you change a query's -arguments or a column's type but don't update your code, it will fail to -compile. - -## Getting Started -Okay, enough hype, let's see it in action. - -First you pass the following SQL to `sqlc generate`: - -```sql -CREATE TABLE authors ( - id BIGSERIAL PRIMARY KEY, - name text NOT NULL, - bio text -); - --- name: GetAuthor :one -SELECT * FROM authors -WHERE id = $1 LIMIT 1; - --- name: ListAuthors :many -SELECT * FROM authors -ORDER BY name; - --- name: CreateAuthor :one -INSERT INTO authors ( - name, bio -) VALUES ( - $1, $2 -) -RETURNING *; - --- name: DeleteAuthor :exec -DELETE FROM authors -WHERE id = $1; -``` - -And then in your application code you'd write: - -```go - -// list all authors -authors, err := db.ListAuthors(ctx) -if err != nil { - return err -} -fmt.Println(authors) - -// create an author -insertedAuthor, err := db.CreateAuthor(ctx, db.CreateAuthorParams{ - Name: "Brian Kernighan", - Bio: sql.NullString{String: "Co-author of The C Programming Language and The Go Programming Language", Valid: true}, -}) -if err != nil { - return err -} -fmt.Println(insertedAuthor) - -// get the author we just inserted -fetchedAuthor, err := db.GetAuthor(ctx, insertedAuthor.ID) -if err != nil { - return err -} -// prints true -fmt.Println(reflect.DeepEqual(insertedAuthor, fetchedAuthor)) -``` - -To make that possible, sqlc generates readable, **idiomatic** Go code that you -otherwise would have had to write yourself. Take a look: - -```go -package db - -import ( - "context" - "database/sql" -) - -type Author struct { - ID int64 - Name string - Bio sql.NullString -} - -const createAuthor = `-- name: CreateAuthor :one -INSERT INTO authors ( - name, bio -) VALUES ( - $1, $2 -) -RETURNING id, name, bio -` - -type CreateAuthorParams struct { - Name string - Bio sql.NullString -} - -func (q *Queries) CreateAuthor(ctx context.Context, arg CreateAuthorParams) (Author, error) { - row := q.db.QueryRowContext(ctx, createAuthor, arg.Name, arg.Bio) - var i Author - err := row.Scan(&i.ID, &i.Name, &i.Bio) - return i, err -} - -const deleteAuthor = `-- name: DeleteAuthor :exec -DELETE FROM authors -WHERE id = $1 -` - -func (q *Queries) DeleteAuthor(ctx context.Context, id int64) error { - _, err := q.db.ExecContext(ctx, deleteAuthor, id) - return err -} - -const getAuthor = `-- name: GetAuthor :one -SELECT id, name, bio FROM authors -WHERE id = $1 LIMIT 1 -` - -func (q *Queries) GetAuthor(ctx context.Context, id int64) (Author, error) { - row := q.db.QueryRowContext(ctx, getAuthor, id) - var i Author - err := row.Scan(&i.ID, &i.Name, &i.Bio) - return i, err -} - -const listAuthors = `-- name: ListAuthors :many -SELECT id, name, bio FROM authors -ORDER BY name -` - -func (q *Queries) ListAuthors(ctx context.Context) ([]Author, error) { - rows, err := q.db.QueryContext(ctx, listAuthors) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Author - for rows.Next() { - var i Author - if err := rows.Scan(&i.ID, &i.Name, &i.Bio); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Close(); err != nil { - return nil, err - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -type DBTX interface { - ExecContext(context.Context, string, ...interface{}) (sql.Result, error) - PrepareContext(context.Context, string) (*sql.Stmt, error) - QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) - QueryRowContext(context.Context, string, ...interface{}) *sql.Row -} - -func New(db DBTX) *Queries { - return &Queries{db: db} -} - -type Queries struct { - db DBTX -} - -func (q *Queries) WithTx(tx *sql.Tx) *Queries { - return &Queries{ - db: tx, - } -} -``` - -## Examples - -Your favorite PostgreSQL / Go features are supported: -- SQL - - [Query annotations](./docs/annotations.md) - - [Transactions](./docs/transactions.md) - - [Prepared queries](./docs/prepared_query.md) - - [Named parameters](./docs/named_parameters.md) - - [SELECT](./docs/query_one.md) - - [NULL](./docs/null.md) - - [COUNT](./docs/query_count.md) - - [INSERT](./docs/insert.md) - - [UPDATE](./docs/update.md) - - [DELETE](./docs/delete.md) - - [RETURNING](./docs/returning.md) - - [ANY](./docs/any.md) -- PostgreSQL Types - - [Arrays](./docs/arrays.md) - - [Enums](./docs/enums.md) - - [Timestamps](./docs/time.md) - - [UUIDs](./docs/uuid.md) -- DDL - - [CREATE TABLE](./docs/table.md) - - [ALTER TABLE](./docs/alter_table.md) -- Go - - [JSON struct tags](./docs/json_tags.md) - - [Migration tools](./docs/migrations.md) - -A full, end-to-end example can be found in the sample -[`ondeck`](./examples/ondeck) package. - -## Usage - -``` -Usage: - sqlc [command] - -Available Commands: - compile Statically check SQL for syntax and type errors - generate Generate Go code from SQL - help Help about any command - init Create an empty sqlc.yaml settings file - version Print the sqlc version number - -Flags: - -h, --help help for sqlc - -Use "sqlc [command] --help" for more information about a command. -``` - -## Settings - -The `sqlc` tool is configured via a `sqlc.yaml` or `sqlc.json` file. This file must be -in the directory where the `sqlc` command is run. - -```yaml -version: "1" -packages: - - name: "db" - path: "internal/db" - queries: "./sql/query/" - schema: "./sql/schema/" - engine: "postgresql" - emit_json_tags: true - emit_prepared_queries: true - emit_interface: false - emit_exact_table_names: false - emit_empty_slices: false -``` - -Each package document has the following keys: -- `name`: - - The package name to use for the generated code. Defaults to `path` basename -- `path`: - - Output directory for generated code -- `queries`: - - Directory of SQL queries or path to single SQL file; or a list of paths -- `schema`: - - Directory of SQL migrations or path to single SQL file; or a list of paths -- `engine`: - - Either `postgresql` or `mysql`. Defaults to `postgresql`. MySQL support is experimental -- `emit_json_tags`: - - If true, add JSON tags to generated structs. Defaults to `false`. -- `emit_db_tags`: - - If true, add DB tags to generated structs. Defaults to `false`. -- `emit_prepared_queries`: - - If true, include support for prepared queries. Defaults to `false`. -- `emit_interface`: - - If true, output a `Querier` interface in the generated package. Defaults to `false`. -- `emit_exact_table_names`: - - If true, struct names will mirror table names. Otherwise, sqlc attempts to singularize plural table names. Defaults to `false`. -- `emit_empty_slices`: - - If true, slices returned by `:many` queries will be empty instead of `nil`. Defaults to `false`. - -### Type Overrides - -The default mapping of PostgreSQL types to Go types only uses packages outside -the standard library when it must. - -For example, the `uuid` PostgreSQL type is mapped to `github.com/google/uuid`. -If a different Go package for UUIDs is required, specify the package in the -`overrides` array. In this case, I'm going to use the `github.com/gofrs/uuid` -instead. - -```yaml -version: "1" -packages: [...] -overrides: - - go_type: "github.com/gofrs/uuid.UUID" - db_type: "uuid" -``` - -Each override document has the following keys: -- `db_type`: - - The PostgreSQL type to override. Find the full list of supported types in [postgresql_type.go](https://github.com/kyleconroy/sqlc/blob/master/internal/codegen/golang/postgresql_type.go#L12). -- `go_type`: - - A fully qualified name to a Go type to use in the generated code. -- `nullable`: - - If true, use this type when a column is nullable. Defaults to `false`. - -### Per-Column Type Overrides - -Sometimes you would like to override the Go type used in model or query generation for -a specific field of a table and not on a type basis as described in the previous section. - -This may be configured by specifying the `column` property in the override definition. `column` -should be of the form `table.column` buy you may be even more specify by specifying `schema.table.column` -or `catalog.schema.table.column`. - -```yaml -version: "1" -packages: [...] -overrides: - - column: "authors.id" - go_type: "github.com/segmentio/ksuid.KSUID" -``` - -### Package Level Overrides - -Overrides can be configured globally, as demonstrated in the previous sections, or they can be configured on a per-package which -scopes the override behavior to just a single package: - -```yaml -version: "1" -packages: - - overrides: [...] -``` - -### Renaming Struct Fields - -Struct field names are generated from column names using a simple algorithm: -split the column name on underscores and capitalize the first letter of each -part. - -``` -account -> Account -spotify_url -> SpotifyUrl -app_id -> AppID -``` - -If you're not happy with a field's generated name, use the `rename` dictionary -to pick a new name. The keys are column names and the values are the struct -field name to use. - -```yaml -version: "1" -packages: [...] -rename: - spotify_url: "SpotifyURL" -``` - -## Installation - -### macOS - -``` -brew install sqlc -``` - -### Ubuntu - -``` -sudo snap install sqlc -``` - -### go get - -``` -go get github.com/kyleconroy/sqlc/cmd/sqlc -``` - -### Docker - -``` -docker pull kjconroy/sqlc -``` - -Run `sqlc` using `docker run`: - -``` -docker run --rm -v $(pwd):/src -w /src kjconroy/sqlc generate -``` - -### Downloads - -Binaries for a given release can be downloaded from the [stable channel on -Equinox](https://dl.equinox.io/sqlc/sqlc/stable) or the latest [GitHub -release](https://github.com/kyleconroy/sqlc/releases). - -### Tip Releases - -Each commit is deployed to the [`devel` channel on Equinox](https://dl.equinox.io/sqlc/sqlc/devel): - -- [Linux](https://bin.equinox.io/c/gvM95th6ps1/sqlc-devel-linux-amd64.tgz) -- [macOS](https://bin.equinox.io/c/gvM95th6ps1/sqlc-devel-darwin-amd64.zip) -- [Windows](https://bin.equinox.io/c/gvM95th6ps1/sqlc-devel-windows-amd64.zip) - -## Other Databases and Languages - -sqlc currently only supports PostgreSQL / Go. MySQL and Kotlin support have -been merged, but both are marked as experimental. SQLite and TypeScript support -are planned. - -| Language | PostgreSQL | MySQL | -| ------------ | :---------------- | :---------------- | -| Go | :white_check_mark: - Stable | :bug: - Beta | -| TypeScript | :timer_clock: - Planned | :timer_clock: - Planned | -| Kotlin | :warning: - Experimental | | - -If you'd like to add another database or language, we'd welcome a contribution. +- [Documentation](https://docs.sqlc.dev) +- [Installation](https://docs.sqlc.dev/en/latest/overview/install.html) +- [Playground](https://play.sqlc.dev) +- [Website](https://sqlc.dev) ## Sponsors -sqlc development is funded by our generous sponsors. +sqlc development is funded by our [generous +sponsors](https://github.com/sponsors/kyleconroy), including the following +companies: -- Companies - - [Meter](https://meter.com) - - [ngrok](https://ngrok.com) - - [Weave](https://www.getweave.com/) -- Individuals - - [Alex Besogonov](https://github.com/Cyberax) - - [Myles McDonnell](https://github.com/myles-mcdonnell) +- [Meter](https://meter.com) +- [ngrok](https://ngrok.com) If you use sqlc at your company, please consider [becoming a sponsor](https://github.com/sponsors/kyleconroy) today. Sponsors receive priority support via the sqlc Slack organization. -## Development - -### Building - -For local development, install `sqlc` under an alias. We suggest `sqlc-dev`. - -``` -go build -o ~/go/bin/sqlc-dev ./cmd/sqlc -``` - -### Running Tests - -``` -go test ./... -``` - -To run the tests in the examples folder, use the `examples` tag. - -``` -go test --tags=examples ./... -``` - -These tests require locally-running database instances. Run these databases -using [Docker Compose](https://docs.docker.com/compose/). - -``` -docker-compose up -d -``` - -The tests use the following environment variables to connect to the -database - -#### For PostgreSQL - -``` -Variable Default Value -------------------------- -PG_HOST 127.0.0.1 -PG_PORT 5432 -PG_USER postgres -PG_PASSWORD mysecretpassword -PG_DATABASE dinotest -``` - -#### For MySQL - -``` -Variable Default Value -------------------------- -MYSQL_HOST 127.0.0.1 -MYSQL_PORT 3306 -MYSQL_USER root -MYSQL_ROOT_PASSWORD mysecretpassword -MYSQL_DATABASE dinotest -``` - -### Regenerate expected test output - -If you need to update a large number of expected test output in the -`internal/endtoend/testdata` directory, run the `regenerate.sh` script. - -``` -make regen -``` - -Note that this uses the `sqlc-dev` binary, not `sqlc` so make sure you have an -up to date `sqlc-dev` binary. - ## Acknowledgements sqlc was inspired by [PugSQL](https://pugsql.org/) and