diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000000..fcbb8b75f5 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,504 @@ +# sqlc: A SQL Compiler + +> And lo, the Great One looked down upon the people and proclaimed: +> +> "SQL is actually pretty great" + +sqlc generates **fully type-safe idiomatic Go 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 + +Seriously, it's that easy. You don't have to write any boilerplate SQL querying +code ever again. + +## Preventing Errors + +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, + } +} +``` + +## 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) + +## 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. + +## Sponsors + +sqlc development is funded by our generous sponsors. + +- 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) + +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 +[HugSQL](https://www.hugsql.org/). diff --git a/docs/conf.py b/docs/conf.py index b93b432911..5db84ed289 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -31,6 +31,7 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ + 'recommonmark', 'sphinx_rtd_theme', ] diff --git a/docs/index.rst b/docs/index.rst index 54e5ffe190..f631d8712a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,11 +10,25 @@ Welcome to sqlc's documentation! :maxdepth: 2 :caption: Contents: - - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` + README.md + alter_table.md + annotations.md + any.md + arrays.md + delete.md + enums.md + insert.md + json_tags.md + migrations.md + named_parameters.md + null.md + prepared_query.md + privacy.md + query_count.md + query_one.md + returning.md + table.md + time.md + transactions.md + update.md + uuid.md diff --git a/docs/requirements.txt b/docs/requirements.txt index d795416b50..409d54778c 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -2,6 +2,7 @@ alabaster==0.7.12 Babel==2.9.0 certifi==2020.12.5 chardet==4.0.0 +commonmark==0.9.1 docutils==0.16 idna==2.10 imagesize==1.2.0 @@ -11,6 +12,7 @@ packaging==20.9 Pygments==2.7.4 pyparsing==2.4.7 pytz==2021.1 +recommonmark==0.7.1 requests==2.25.1 snowballstemmer==2.1.0 Sphinx==3.4.3