From b6f785f579c099c740178b7c3d69c9e7cd162d4c Mon Sep 17 00:00:00 2001 From: Kyle Gray Date: Tue, 5 Sep 2023 17:57:50 -0700 Subject: [PATCH] docs: Document sqlc.* macros (#2698) Add a new section on embedding structs --- docs/howto/embedding.md | 61 ++++++++++++++++++++ docs/howto/insert.md | 38 ++++++++++++- docs/index.rst | 8 ++- docs/reference/macros.md | 119 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 docs/howto/embedding.md create mode 100644 docs/reference/macros.md diff --git a/docs/howto/embedding.md b/docs/howto/embedding.md new file mode 100644 index 0000000000..125386135b --- /dev/null +++ b/docs/howto/embedding.md @@ -0,0 +1,61 @@ +#### Embedding structs + +Embedding allows you to reuse existing model structs in more queries, resulting +in less manual serialization work. First, imagine we have the following schema +with students and test scores. + +```sql +CREATE TABLE students ( + id bigserial PRIMARY KEY, + name text NOT NULL, + age integer NOT NULL +); + +CREATE TABLE test_scores ( + student_id bigint NOT NULL, + score integer NOT NULL, + grade text NOT NULL +); +``` + +We want to select the student record and the scores they got on a test. +Here's how we'd usually do that: + +```sql +-- name: ScoreAndTests :many +SELECT students.*, test_scores.* +FROM students +JOIN test_scores ON test_scores.student_id = students.id +WHERE students.id = ?; +``` + +When using Go, sqlc will produce a struct like this: + +```go +type ScoreAndTestsRow struct { + ID int64 + Name string + Age int32 + StudentID int64 + Score int32 + Grade string +} +``` + +With embedding, the struct will contain a model for both tables instead of a +flattened list of columns. + +```sql +-- name: ScoreAndTests :many +SELECT sqlc.embed(students), sqlc.embed(test_scores) +FROM students +JOIN test_scores ON test_scores.student_id = students.id +WHERE students.id = ?; +``` + +``` +type ScoreAndTestsRow struct { + Student Student + TestScore TestScore +} +``` \ No newline at end of file diff --git a/docs/howto/insert.md b/docs/howto/insert.md index 4aae9e3364..cc923e0559 100644 --- a/docs/howto/insert.md +++ b/docs/howto/insert.md @@ -122,6 +122,8 @@ func (q *Queries) CreateAuthorAndReturnId(ctx context.Context, arg CreateAuthorA ## Using CopyFrom +### PostgreSQL + PostgreSQL supports the [COPY protocol](https://www.postgresql.org/docs/current/sql-copy.html) that can insert rows a lot faster than sequential inserts. You can use this easily with sqlc: ```sql @@ -146,7 +148,25 @@ func (q *Queries) CreateAuthors(ctx context.Context, arg []CreateAuthorsParams) } ``` -MySQL supports a similar feature using [LOAD DATA](https://dev.mysql.com/doc/refman/8.0/en/load-data.html). +The `:copyfrom` command requires either `pgx/v4` or `pgx/v5`. + +```yaml +version: "2" +sql: + - engine: "postgresql" + queries: "query.sql" + schema: "query.sql" + gen: + go: + package: "db" + sql_package: "pgx/v5" + out: "db" +``` + +### MySQL + +### MySQL supports a similar feature using [LOAD DATA](https://dev.mysql.com/doc/refman/8.0/en/load-data.html). + Errors and duplicate keys are treated as warnings and insertion will continue, even without an error for some cases. Use this in a transaction @@ -165,4 +185,20 @@ INSERT INTO foo (a, b, c, d) VALUES (?, ?, ?, ?); func (q *Queries) InsertValues(ctx context.Context, arg []InsertValuesParams) (int64, error) { ... } +``` + +The `:copyfrom` command requires setting the `sql_package` and `sql_driver` options. + +```yaml +version: "2" +sql: + - engine: "mysql" + queries: "query.sql" + schema: "query.sql" + gen: + go: + package: "db" + sql_package: "database/sql" + sql_driver: "github.com/go-sql-driver/mysql" + out: "db" ``` \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index 3ad59b1e18..25d70e7eed 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -53,6 +53,7 @@ code ever again. howto/ddl.md howto/structs.md + howto/embedding.md howto/vet.md howto/ci-cd.md @@ -63,13 +64,14 @@ code ever again. :caption: Reference :hidden: + reference/changelog.md reference/cli.md reference/config.md reference/datatypes.md - reference/query-annotations.md - reference/language-support.rst reference/environment-variables.md - reference/changelog.md + reference/language-support.rst + reference/macros.md + reference/query-annotations.md .. toctree:: :maxdepth: 2 diff --git a/docs/reference/macros.md b/docs/reference/macros.md new file mode 100644 index 0000000000..7c8bc67842 --- /dev/null +++ b/docs/reference/macros.md @@ -0,0 +1,119 @@ +# Macros + +## `sqlc.arg` + +Attach a name to a parameter in a SQL query. This macro expands to an +engine-specific parameter placeholder. The name of the parameter is noted and +used during code generation. + +```sql +-- name: GetAuthorByName :one +SELECT * +FROM authors +WHERE lower(name) = sqlc.arg(name); + +-- >>> EXPANDS TO >>> + +-- name: GetAuthorByName :one +SELECT * +FROM authors +WHERE lower(name) = ?; +``` + +See more examples in [Naming parameters](../howto/named_parameters). + +## `sqlc.embed` + +Embedding allows you to reuse existing model structs in more queries, resulting +in less manual serialization work. First, imagine we have the following schema +with students and test scores. + +```sql +CREATE TABLE students ( + id bigserial PRIMARY KEY, + name text, + age integer +); + +CREATE TABLE test_scores ( + student_id bigint, + score integer, + grade text +); +``` + +```sql +-- name: GetStudentAndScore :one +SELECT sqlc.embed(students), sqlc.embed(test_scores) +FROM students +JOIN test_scores ON test_scores.student_id = students.id +WHERE students.id = $1; + +-- >>> EXPANDS TO >>> + +-- name: GetStudentAndScore :one +SELECT students.*, test_scores.* +FROM students +JOIN test_scores ON test_scores.student_id = students.id +WHERE students.id = $1; +``` + +The Go method will return a struct with a field for the `Student` and field for +the test `TestScore` instead of each column existing on the struct. + +```go +type GetStudentAndScoreRow struct { + Student Student + TestScore TestScore +} + +func (q *Queries) GetStudentAndScore(ctx context.Context, id int64) (GetStudentAndScoreRow, error) { + // ... +} +``` + +See a full example in [Embedding structs](../howto/embedding). + +## `sqlc.narg` + +The same as `sqlc.arg`, but always marks the parameter as nullable. + +```sql +-- name: GetAuthorByName :one +SELECT * +FROM authors +WHERE lower(name) = sqlc.narg(name); + +-- >>> EXPANDS TO >>> + +-- name: GetAuthorByName :one +SELECT * +FROM authors +WHERE LOWER(name) = ?; +``` + +See more examples in [Naming parameters](../howto/named_parameters). + +## `sqlc.slice` + +For drivers that do not support passing slices to the IN operator, the +`sqlc.slice` macro generates a dynamic query at runtime with the correct +number of parameters. + +```sql +/* name: SelectStudents :many */ +SELECT * FROM students +WHERE age IN (sqlc.slice("ages")) + +-- >>> EXPANDS TO >>> + +/* name: SelectStudents :many */ +SELECT id, name, age FROM authors +WHERE age IN (/*SLICE:ages*/?) +``` + +Since the `/*SLICE:ages*/` placeholder is dynamically replaced on a per-query +basis, this macro can't be used with prepared statements. + +See a full example in [Passing a slice as a parameter to a +query](../howto/select.html#mysql-and-sqlite). \ No newline at end of file